@paceful/sdk 1.0.0
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 +433 -0
- package/dist/index.d.mts +1052 -0
- package/dist/index.d.ts +1052 -0
- package/dist/index.js +1968 -0
- package/dist/index.mjs +1938 -0
- package/package.json +62 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1938 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
6
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
7
|
+
}) : x)(function(x) {
|
|
8
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
9
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
10
|
+
});
|
|
11
|
+
var __esm = (fn, res) => function __init() {
|
|
12
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
13
|
+
};
|
|
14
|
+
var __export = (target, all) => {
|
|
15
|
+
for (var name in all)
|
|
16
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
17
|
+
};
|
|
18
|
+
var __copyProps = (to, from, except, desc) => {
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
|
+
for (let key of __getOwnPropNames(from))
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
22
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
};
|
|
26
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
|
+
|
|
28
|
+
// src/widgets/PacefulProvider.tsx
|
|
29
|
+
var PacefulProvider_exports = {};
|
|
30
|
+
__export(PacefulProvider_exports, {
|
|
31
|
+
PacefulProvider: () => PacefulProvider,
|
|
32
|
+
usePaceful: () => usePaceful
|
|
33
|
+
});
|
|
34
|
+
import React, { createContext, useContext, useMemo } from "react";
|
|
35
|
+
function usePaceful() {
|
|
36
|
+
const ctx = useContext(PacefulContext);
|
|
37
|
+
if (!ctx) throw new Error("Wrap your component tree with <PacefulProvider>");
|
|
38
|
+
return ctx;
|
|
39
|
+
}
|
|
40
|
+
function PacefulProvider({ apiKey, userId, baseUrl, children }) {
|
|
41
|
+
const value = useMemo(() => ({
|
|
42
|
+
apiKey,
|
|
43
|
+
userId,
|
|
44
|
+
baseUrl: baseUrl || "https://paceful-app.vercel.app/api/v1/partner"
|
|
45
|
+
}), [apiKey, userId, baseUrl]);
|
|
46
|
+
return React.createElement(PacefulContext.Provider, { value }, children);
|
|
47
|
+
}
|
|
48
|
+
var PacefulContext;
|
|
49
|
+
var init_PacefulProvider = __esm({
|
|
50
|
+
"src/widgets/PacefulProvider.tsx"() {
|
|
51
|
+
"use strict";
|
|
52
|
+
PacefulContext = createContext(null);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// src/utils/errors.ts
|
|
57
|
+
var PacefulError = class _PacefulError extends Error {
|
|
58
|
+
constructor(message, code, statusCode, details) {
|
|
59
|
+
super(message);
|
|
60
|
+
this.name = "PacefulError";
|
|
61
|
+
this.code = code;
|
|
62
|
+
this.statusCode = statusCode;
|
|
63
|
+
this.details = details;
|
|
64
|
+
if (Error.captureStackTrace) {
|
|
65
|
+
Error.captureStackTrace(this, _PacefulError);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
var PacefulAuthError = class extends PacefulError {
|
|
70
|
+
constructor(message = "Invalid or expired API key") {
|
|
71
|
+
super(message, "UNAUTHORIZED", 401);
|
|
72
|
+
this.name = "PacefulAuthError";
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
var PacefulRateLimitError = class extends PacefulError {
|
|
76
|
+
constructor(retryAfter = 3600) {
|
|
77
|
+
super("Rate limit exceeded", "RATE_LIMITED", 429);
|
|
78
|
+
this.name = "PacefulRateLimitError";
|
|
79
|
+
this.retryAfter = retryAfter;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
var PacefulNotFoundError = class extends PacefulError {
|
|
83
|
+
constructor(resource = "Resource") {
|
|
84
|
+
super(`${resource} not found`, "NOT_FOUND", 404);
|
|
85
|
+
this.name = "PacefulNotFoundError";
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
var PacefulValidationError = class extends PacefulError {
|
|
89
|
+
constructor(message, details) {
|
|
90
|
+
super(message, "BAD_REQUEST", 400, details);
|
|
91
|
+
this.name = "PacefulValidationError";
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
var PacefulNetworkError = class extends PacefulError {
|
|
95
|
+
constructor(message = "Network request failed") {
|
|
96
|
+
super(message, "NETWORK_ERROR");
|
|
97
|
+
this.name = "PacefulNetworkError";
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
function parseApiError(statusCode, body, headers) {
|
|
101
|
+
const message = body.error?.message || body.message || "Unknown error";
|
|
102
|
+
const code = body.error?.code || "UNKNOWN_ERROR";
|
|
103
|
+
switch (statusCode) {
|
|
104
|
+
case 401:
|
|
105
|
+
return new PacefulAuthError(message);
|
|
106
|
+
case 404:
|
|
107
|
+
return new PacefulNotFoundError(message);
|
|
108
|
+
case 429: {
|
|
109
|
+
const retryAfter = headers?.get("Retry-After");
|
|
110
|
+
return new PacefulRateLimitError(
|
|
111
|
+
retryAfter ? parseInt(retryAfter, 10) : 3600
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
case 400:
|
|
115
|
+
return new PacefulValidationError(message);
|
|
116
|
+
default:
|
|
117
|
+
return new PacefulError(message, code, statusCode);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// src/utils/http.ts
|
|
122
|
+
var DEFAULT_BASE_URL = "https://api.paceful.app";
|
|
123
|
+
var DEFAULT_TIMEOUT = 3e4;
|
|
124
|
+
var DEFAULT_RETRIES = 3;
|
|
125
|
+
var HttpClient = class {
|
|
126
|
+
constructor(config) {
|
|
127
|
+
if (!config.apiKey) {
|
|
128
|
+
throw new PacefulError("API key is required", "CONFIGURATION_ERROR");
|
|
129
|
+
}
|
|
130
|
+
this.apiKey = config.apiKey;
|
|
131
|
+
this.baseUrl = (config.baseUrl || DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
132
|
+
this.timeout = config.timeout || DEFAULT_TIMEOUT;
|
|
133
|
+
this.maxRetries = config.retries ?? DEFAULT_RETRIES;
|
|
134
|
+
}
|
|
135
|
+
async request(options) {
|
|
136
|
+
const { method, path, body, query } = options;
|
|
137
|
+
let url = `${this.baseUrl}${path}`;
|
|
138
|
+
if (query) {
|
|
139
|
+
const params = new URLSearchParams();
|
|
140
|
+
for (const [key, value] of Object.entries(query)) {
|
|
141
|
+
if (value !== void 0) {
|
|
142
|
+
params.append(key, String(value));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
const queryString = params.toString();
|
|
146
|
+
if (queryString) {
|
|
147
|
+
url += `?${queryString}`;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
const headers = {
|
|
151
|
+
"Content-Type": "application/json",
|
|
152
|
+
"X-API-Key": this.apiKey,
|
|
153
|
+
"User-Agent": "@paceful/sdk/1.0.0"
|
|
154
|
+
};
|
|
155
|
+
const fetchOptions = {
|
|
156
|
+
method,
|
|
157
|
+
headers
|
|
158
|
+
};
|
|
159
|
+
if (body && method !== "GET") {
|
|
160
|
+
fetchOptions.body = JSON.stringify(body);
|
|
161
|
+
}
|
|
162
|
+
let lastError = null;
|
|
163
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
164
|
+
try {
|
|
165
|
+
const controller = new AbortController();
|
|
166
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
167
|
+
fetchOptions.signal = controller.signal;
|
|
168
|
+
const response = await fetch(url, fetchOptions);
|
|
169
|
+
clearTimeout(timeoutId);
|
|
170
|
+
const responseBody = await response.json();
|
|
171
|
+
if (!response.ok) {
|
|
172
|
+
const error = parseApiError(
|
|
173
|
+
response.status,
|
|
174
|
+
responseBody,
|
|
175
|
+
response.headers
|
|
176
|
+
);
|
|
177
|
+
if (response.status < 500 && response.status !== 429) {
|
|
178
|
+
throw error;
|
|
179
|
+
}
|
|
180
|
+
lastError = error;
|
|
181
|
+
if (attempt < this.maxRetries) {
|
|
182
|
+
const backoffMs = Math.min(1e3 * Math.pow(2, attempt), 1e4);
|
|
183
|
+
await this.sleep(backoffMs);
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
throw error;
|
|
187
|
+
}
|
|
188
|
+
if (!responseBody.success) {
|
|
189
|
+
throw new PacefulError(
|
|
190
|
+
responseBody.error?.message || "Request failed",
|
|
191
|
+
responseBody.error?.code || "UNKNOWN_ERROR"
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
return responseBody.data;
|
|
195
|
+
} catch (error) {
|
|
196
|
+
if (error instanceof PacefulError) {
|
|
197
|
+
throw error;
|
|
198
|
+
}
|
|
199
|
+
if (error instanceof Error) {
|
|
200
|
+
if (error.name === "AbortError") {
|
|
201
|
+
lastError = new PacefulNetworkError("Request timed out");
|
|
202
|
+
} else {
|
|
203
|
+
lastError = new PacefulNetworkError(error.message);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (attempt < this.maxRetries) {
|
|
207
|
+
const backoffMs = Math.min(1e3 * Math.pow(2, attempt), 1e4);
|
|
208
|
+
await this.sleep(backoffMs);
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
throw lastError || new PacefulNetworkError();
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
throw lastError || new PacefulNetworkError();
|
|
215
|
+
}
|
|
216
|
+
sleep(ms) {
|
|
217
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
218
|
+
}
|
|
219
|
+
// Convenience methods
|
|
220
|
+
async get(path, query) {
|
|
221
|
+
return this.request({ method: "GET", path, query });
|
|
222
|
+
}
|
|
223
|
+
async post(path, body) {
|
|
224
|
+
return this.request({ method: "POST", path, body });
|
|
225
|
+
}
|
|
226
|
+
async put(path, body) {
|
|
227
|
+
return this.request({ method: "PUT", path, body });
|
|
228
|
+
}
|
|
229
|
+
async patch(path, body) {
|
|
230
|
+
return this.request({ method: "PATCH", path, body });
|
|
231
|
+
}
|
|
232
|
+
async delete(path) {
|
|
233
|
+
return this.request({ method: "DELETE", path });
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
// src/modules/users.ts
|
|
238
|
+
var Users = class {
|
|
239
|
+
constructor(http) {
|
|
240
|
+
this.http = http;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Register a new user or update existing user metadata
|
|
244
|
+
*
|
|
245
|
+
* @param params - User registration parameters
|
|
246
|
+
* @returns The registered or updated user
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* ```typescript
|
|
250
|
+
* const user = await paceful.users.register({
|
|
251
|
+
* externalId: 'user_123',
|
|
252
|
+
* email: 'user@example.com',
|
|
253
|
+
* metadata: { plan: 'premium' }
|
|
254
|
+
* });
|
|
255
|
+
* ```
|
|
256
|
+
*/
|
|
257
|
+
async register(params) {
|
|
258
|
+
return this.http.post("/api/v1/partner/users", {
|
|
259
|
+
externalId: params.externalId,
|
|
260
|
+
email: params.email,
|
|
261
|
+
metadata: params.metadata
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Get a user by their external ID
|
|
266
|
+
*
|
|
267
|
+
* @param externalId - Your system's user identifier
|
|
268
|
+
* @returns The user if found
|
|
269
|
+
*
|
|
270
|
+
* @example
|
|
271
|
+
* ```typescript
|
|
272
|
+
* const user = await paceful.users.get('user_123');
|
|
273
|
+
* ```
|
|
274
|
+
*/
|
|
275
|
+
async get(externalId) {
|
|
276
|
+
return this.http.get(`/api/v1/partner/users/${externalId}`);
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Update a user's metadata
|
|
280
|
+
*
|
|
281
|
+
* @param externalId - Your system's user identifier
|
|
282
|
+
* @param metadata - New metadata to merge with existing
|
|
283
|
+
* @returns The updated user
|
|
284
|
+
*
|
|
285
|
+
* @example
|
|
286
|
+
* ```typescript
|
|
287
|
+
* const user = await paceful.users.update('user_123', {
|
|
288
|
+
* metadata: { plan: 'enterprise' }
|
|
289
|
+
* });
|
|
290
|
+
* ```
|
|
291
|
+
*/
|
|
292
|
+
async update(externalId, params) {
|
|
293
|
+
return this.http.patch(`/api/v1/partner/users/${externalId}`, params);
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* List all users for this partner
|
|
297
|
+
*
|
|
298
|
+
* @param params - Pagination parameters
|
|
299
|
+
* @returns Paginated list of users
|
|
300
|
+
*
|
|
301
|
+
* @example
|
|
302
|
+
* ```typescript
|
|
303
|
+
* const { items, total, hasMore } = await paceful.users.list({ limit: 50 });
|
|
304
|
+
* ```
|
|
305
|
+
*/
|
|
306
|
+
async list(params) {
|
|
307
|
+
return this.http.get("/api/v1/partner/users", {
|
|
308
|
+
limit: params?.limit,
|
|
309
|
+
cursor: params?.cursor
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Delete a user and all their data
|
|
314
|
+
*
|
|
315
|
+
* @param externalId - Your system's user identifier
|
|
316
|
+
*
|
|
317
|
+
* @example
|
|
318
|
+
* ```typescript
|
|
319
|
+
* await paceful.users.delete('user_123');
|
|
320
|
+
* ```
|
|
321
|
+
*/
|
|
322
|
+
async delete(externalId) {
|
|
323
|
+
await this.http.delete(`/api/v1/partner/users/${externalId}`);
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
// src/modules/mood.ts
|
|
328
|
+
var Mood = class {
|
|
329
|
+
constructor(http) {
|
|
330
|
+
this.http = http;
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Log a mood entry for a user
|
|
334
|
+
*
|
|
335
|
+
* @param params - Mood log parameters
|
|
336
|
+
* @returns The created mood entry
|
|
337
|
+
*
|
|
338
|
+
* @example
|
|
339
|
+
* ```typescript
|
|
340
|
+
* const entry = await paceful.mood.log({
|
|
341
|
+
* externalId: 'user_123',
|
|
342
|
+
* mood: 4,
|
|
343
|
+
* note: 'Feeling productive today',
|
|
344
|
+
* factors: ['work', 'exercise']
|
|
345
|
+
* });
|
|
346
|
+
* ```
|
|
347
|
+
*/
|
|
348
|
+
async log(params) {
|
|
349
|
+
return this.http.post("/api/v1/partner/mood", {
|
|
350
|
+
externalId: params.externalId,
|
|
351
|
+
mood: params.mood,
|
|
352
|
+
note: params.note,
|
|
353
|
+
factors: params.factors,
|
|
354
|
+
timestamp: params.timestamp
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Get mood history for a user
|
|
359
|
+
*
|
|
360
|
+
* @param params - History query parameters
|
|
361
|
+
* @returns List of mood entries
|
|
362
|
+
*
|
|
363
|
+
* @example
|
|
364
|
+
* ```typescript
|
|
365
|
+
* const history = await paceful.mood.history({
|
|
366
|
+
* externalId: 'user_123',
|
|
367
|
+
* startDate: '2024-01-01',
|
|
368
|
+
* limit: 30
|
|
369
|
+
* });
|
|
370
|
+
* ```
|
|
371
|
+
*/
|
|
372
|
+
async history(params) {
|
|
373
|
+
return this.http.get(`/api/v1/partner/mood/${params.externalId}/history`, {
|
|
374
|
+
startDate: params.startDate,
|
|
375
|
+
endDate: params.endDate,
|
|
376
|
+
limit: params.limit
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Get the latest mood entry for a user
|
|
381
|
+
*
|
|
382
|
+
* @param externalId - Your system's user identifier
|
|
383
|
+
* @returns The most recent mood entry or null
|
|
384
|
+
*
|
|
385
|
+
* @example
|
|
386
|
+
* ```typescript
|
|
387
|
+
* const latest = await paceful.mood.latest('user_123');
|
|
388
|
+
* if (latest) {
|
|
389
|
+
* console.log(`Last mood: ${latest.mood}`);
|
|
390
|
+
* }
|
|
391
|
+
* ```
|
|
392
|
+
*/
|
|
393
|
+
async latest(externalId) {
|
|
394
|
+
return this.http.get(`/api/v1/partner/mood/${externalId}/latest`);
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Get mood statistics for a user over a time period
|
|
398
|
+
*
|
|
399
|
+
* @param externalId - Your system's user identifier
|
|
400
|
+
* @param period - Time period ('7d', '30d', '90d', 'all')
|
|
401
|
+
* @returns Mood statistics
|
|
402
|
+
*
|
|
403
|
+
* @example
|
|
404
|
+
* ```typescript
|
|
405
|
+
* const stats = await paceful.mood.stats('user_123', '30d');
|
|
406
|
+
* console.log(`Average mood: ${stats.average}`);
|
|
407
|
+
* ```
|
|
408
|
+
*/
|
|
409
|
+
async stats(externalId, period = "30d") {
|
|
410
|
+
return this.http.get(`/api/v1/partner/mood/${externalId}/stats`, { period });
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
// src/modules/journal.ts
|
|
415
|
+
var Journal = class {
|
|
416
|
+
constructor(http) {
|
|
417
|
+
this.http = http;
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Create a journal entry for a user
|
|
421
|
+
*
|
|
422
|
+
* @param params - Journal entry parameters
|
|
423
|
+
* @returns The created journal entry with AI analysis
|
|
424
|
+
*
|
|
425
|
+
* @example
|
|
426
|
+
* ```typescript
|
|
427
|
+
* const entry = await paceful.journal.create({
|
|
428
|
+
* externalId: 'user_123',
|
|
429
|
+
* content: 'Today I reflected on my progress...',
|
|
430
|
+
* mood: 4
|
|
431
|
+
* });
|
|
432
|
+
*
|
|
433
|
+
* console.log('Themes:', entry.themes);
|
|
434
|
+
* console.log('Sentiment:', entry.sentimentScore);
|
|
435
|
+
* ```
|
|
436
|
+
*/
|
|
437
|
+
async create(params) {
|
|
438
|
+
return this.http.post("/api/v1/partner/journal", {
|
|
439
|
+
externalId: params.externalId,
|
|
440
|
+
content: params.content,
|
|
441
|
+
promptId: params.promptId,
|
|
442
|
+
mood: params.mood
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Get journal history for a user
|
|
447
|
+
*
|
|
448
|
+
* @param params - History query parameters
|
|
449
|
+
* @returns List of journal entries
|
|
450
|
+
*
|
|
451
|
+
* @example
|
|
452
|
+
* ```typescript
|
|
453
|
+
* const entries = await paceful.journal.history({
|
|
454
|
+
* externalId: 'user_123',
|
|
455
|
+
* limit: 10
|
|
456
|
+
* });
|
|
457
|
+
* ```
|
|
458
|
+
*/
|
|
459
|
+
async history(params) {
|
|
460
|
+
return this.http.get(`/api/v1/partner/journal/${params.externalId}/history`, {
|
|
461
|
+
startDate: params.startDate,
|
|
462
|
+
endDate: params.endDate,
|
|
463
|
+
limit: params.limit
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Get a specific journal entry
|
|
468
|
+
*
|
|
469
|
+
* @param externalId - Your system's user identifier
|
|
470
|
+
* @param entryId - The journal entry ID
|
|
471
|
+
* @returns The journal entry
|
|
472
|
+
*
|
|
473
|
+
* @example
|
|
474
|
+
* ```typescript
|
|
475
|
+
* const entry = await paceful.journal.get('user_123', 'entry_abc');
|
|
476
|
+
* ```
|
|
477
|
+
*/
|
|
478
|
+
async get(externalId, entryId) {
|
|
479
|
+
return this.http.get(`/api/v1/partner/journal/${externalId}/${entryId}`);
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Get available journal prompts
|
|
483
|
+
*
|
|
484
|
+
* @returns List of prompts categorized by theme
|
|
485
|
+
*
|
|
486
|
+
* @example
|
|
487
|
+
* ```typescript
|
|
488
|
+
* const prompts = await paceful.journal.prompts();
|
|
489
|
+
* const gratitudePrompts = prompts.gratitude;
|
|
490
|
+
* ```
|
|
491
|
+
*/
|
|
492
|
+
async prompts() {
|
|
493
|
+
return this.http.get("/api/v1/partner/journal/prompts");
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Get journal statistics for a user
|
|
497
|
+
*
|
|
498
|
+
* @param externalId - Your system's user identifier
|
|
499
|
+
* @param period - Time period ('7d', '30d', '90d', 'all')
|
|
500
|
+
* @returns Journal statistics
|
|
501
|
+
*
|
|
502
|
+
* @example
|
|
503
|
+
* ```typescript
|
|
504
|
+
* const stats = await paceful.journal.stats('user_123', '30d');
|
|
505
|
+
* console.log(`Entries this month: ${stats.count}`);
|
|
506
|
+
* ```
|
|
507
|
+
*/
|
|
508
|
+
async stats(externalId, period = "30d") {
|
|
509
|
+
return this.http.get(`/api/v1/partner/journal/${externalId}/stats`, { period });
|
|
510
|
+
}
|
|
511
|
+
};
|
|
512
|
+
|
|
513
|
+
// src/modules/ers.ts
|
|
514
|
+
var ERS = class {
|
|
515
|
+
constructor(http) {
|
|
516
|
+
this.http = http;
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* Get the current ERS score for a user
|
|
520
|
+
* Returns cached score if recent (< 24 hours), otherwise calculates fresh
|
|
521
|
+
*
|
|
522
|
+
* @param externalId - Your system's user identifier
|
|
523
|
+
* @returns The user's ERS score with trend data
|
|
524
|
+
*
|
|
525
|
+
* @example
|
|
526
|
+
* ```typescript
|
|
527
|
+
* const ers = await paceful.ers.get('user_123');
|
|
528
|
+
*
|
|
529
|
+
* console.log(`ERS Score: ${ers.ersScore}`);
|
|
530
|
+
* console.log(`Stage: ${ers.stage}`);
|
|
531
|
+
* console.log(`Trend: ${ers.trend?.direction}`);
|
|
532
|
+
* ```
|
|
533
|
+
*/
|
|
534
|
+
async get(externalId) {
|
|
535
|
+
return this.http.get(`/api/v1/partner/ers/${externalId}`);
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Force a fresh ERS calculation for a user
|
|
539
|
+
* Useful after significant user activity
|
|
540
|
+
*
|
|
541
|
+
* @param externalId - Your system's user identifier
|
|
542
|
+
* @returns Fresh calculation results with change data
|
|
543
|
+
*
|
|
544
|
+
* @example
|
|
545
|
+
* ```typescript
|
|
546
|
+
* const result = await paceful.ers.calculate('user_123');
|
|
547
|
+
*
|
|
548
|
+
* if (result.change !== null) {
|
|
549
|
+
* console.log(`Score changed by ${result.change} points`);
|
|
550
|
+
* }
|
|
551
|
+
* ```
|
|
552
|
+
*/
|
|
553
|
+
async calculate(externalId) {
|
|
554
|
+
return this.http.post("/api/v1/partner/ers/calculate", {
|
|
555
|
+
externalId
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* Get ERS scores for multiple users in a single request
|
|
560
|
+
* Maximum 50 users per request
|
|
561
|
+
*
|
|
562
|
+
* @param externalIds - Array of your system's user identifiers
|
|
563
|
+
* @returns Batch results with scores for each user
|
|
564
|
+
*
|
|
565
|
+
* @example
|
|
566
|
+
* ```typescript
|
|
567
|
+
* const batch = await paceful.ers.batch(['user_1', 'user_2', 'user_3']);
|
|
568
|
+
*
|
|
569
|
+
* for (const result of batch.results) {
|
|
570
|
+
* console.log(`${result.externalId}: ${result.ersScore}`);
|
|
571
|
+
* }
|
|
572
|
+
* ```
|
|
573
|
+
*/
|
|
574
|
+
async batch(externalIds) {
|
|
575
|
+
return this.http.post("/api/v1/partner/ers/batch", {
|
|
576
|
+
externalIds
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Get ERS score history for a user
|
|
581
|
+
*
|
|
582
|
+
* @param externalId - Your system's user identifier
|
|
583
|
+
* @param params - Query parameters
|
|
584
|
+
* @returns Historical ERS scores
|
|
585
|
+
*
|
|
586
|
+
* @example
|
|
587
|
+
* ```typescript
|
|
588
|
+
* const history = await paceful.ers.history('user_123', {
|
|
589
|
+
* period: '30d',
|
|
590
|
+
* granularity: 'daily'
|
|
591
|
+
* });
|
|
592
|
+
* ```
|
|
593
|
+
*/
|
|
594
|
+
async history(externalId, params) {
|
|
595
|
+
return this.http.get(`/api/v1/partner/ers/${externalId}/history`, {
|
|
596
|
+
period: params?.period,
|
|
597
|
+
granularity: params?.granularity
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* Set up alerts for ERS threshold crossings
|
|
602
|
+
*
|
|
603
|
+
* @param params - Alert configuration
|
|
604
|
+
* @returns The created alert configuration
|
|
605
|
+
*
|
|
606
|
+
* @example
|
|
607
|
+
* ```typescript
|
|
608
|
+
* await paceful.ers.setThresholdAlert({
|
|
609
|
+
* externalId: 'user_123',
|
|
610
|
+
* threshold: 40,
|
|
611
|
+
* direction: 'below',
|
|
612
|
+
* webhookUrl: 'https://yourapp.com/alerts'
|
|
613
|
+
* });
|
|
614
|
+
* ```
|
|
615
|
+
*/
|
|
616
|
+
async setThresholdAlert(params) {
|
|
617
|
+
return this.http.post("/api/v1/partner/ers/alerts", params);
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
|
|
621
|
+
// src/modules/analytics.ts
|
|
622
|
+
var Analytics = class {
|
|
623
|
+
constructor(http) {
|
|
624
|
+
this.http = http;
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* Get aggregate analytics summary for all your users
|
|
628
|
+
*
|
|
629
|
+
* @param period - Time period for analytics ('7d', '30d', '90d', 'all')
|
|
630
|
+
* @returns Aggregate analytics summary
|
|
631
|
+
*
|
|
632
|
+
* @example
|
|
633
|
+
* ```typescript
|
|
634
|
+
* const summary = await paceful.analytics.summary('30d');
|
|
635
|
+
*
|
|
636
|
+
* console.log(`Total users: ${summary.totalUsers}`);
|
|
637
|
+
* console.log(`Average ERS: ${summary.averageErs}`);
|
|
638
|
+
* console.log(`Healing stage: ${summary.stageDistribution.healing * 100}%`);
|
|
639
|
+
* ```
|
|
640
|
+
*/
|
|
641
|
+
async summary(period = "30d") {
|
|
642
|
+
return this.http.get("/api/v1/partner/analytics/summary", {
|
|
643
|
+
period
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* Get engagement metrics over time
|
|
648
|
+
*
|
|
649
|
+
* @param params - Query parameters
|
|
650
|
+
* @returns Time series engagement data
|
|
651
|
+
*
|
|
652
|
+
* @example
|
|
653
|
+
* ```typescript
|
|
654
|
+
* const engagement = await paceful.analytics.engagement({
|
|
655
|
+
* period: '30d',
|
|
656
|
+
* granularity: 'daily'
|
|
657
|
+
* });
|
|
658
|
+
*
|
|
659
|
+
* for (const point of engagement.dataPoints) {
|
|
660
|
+
* console.log(`${point.date}: ${point.activeUsers} active users`);
|
|
661
|
+
* }
|
|
662
|
+
* ```
|
|
663
|
+
*/
|
|
664
|
+
async engagement(params) {
|
|
665
|
+
return this.http.get("/api/v1/partner/analytics/engagement", {
|
|
666
|
+
period: params?.period,
|
|
667
|
+
granularity: params?.granularity
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Get ERS distribution across all users
|
|
672
|
+
*
|
|
673
|
+
* @param period - Time period for analytics
|
|
674
|
+
* @returns ERS distribution data
|
|
675
|
+
*
|
|
676
|
+
* @example
|
|
677
|
+
* ```typescript
|
|
678
|
+
* const distribution = await paceful.analytics.ersDistribution('30d');
|
|
679
|
+
*
|
|
680
|
+
* console.log('Score ranges:');
|
|
681
|
+
* for (const bucket of distribution.buckets) {
|
|
682
|
+
* console.log(`${bucket.range}: ${bucket.count} users`);
|
|
683
|
+
* }
|
|
684
|
+
* ```
|
|
685
|
+
*/
|
|
686
|
+
async ersDistribution(period = "30d") {
|
|
687
|
+
return this.http.get("/api/v1/partner/analytics/ers-distribution", {
|
|
688
|
+
period
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Get retention metrics
|
|
693
|
+
*
|
|
694
|
+
* @param period - Time period for analytics
|
|
695
|
+
* @returns Retention and churn data
|
|
696
|
+
*
|
|
697
|
+
* @example
|
|
698
|
+
* ```typescript
|
|
699
|
+
* const retention = await paceful.analytics.retention('30d');
|
|
700
|
+
*
|
|
701
|
+
* console.log(`Day 7 retention: ${retention.day7}%`);
|
|
702
|
+
* console.log(`Day 30 retention: ${retention.day30}%`);
|
|
703
|
+
* ```
|
|
704
|
+
*/
|
|
705
|
+
async retention(period = "30d") {
|
|
706
|
+
return this.http.get("/api/v1/partner/analytics/retention", { period });
|
|
707
|
+
}
|
|
708
|
+
/**
|
|
709
|
+
* Export analytics data as CSV
|
|
710
|
+
*
|
|
711
|
+
* @param params - Export parameters
|
|
712
|
+
* @returns Download URL for the CSV
|
|
713
|
+
*
|
|
714
|
+
* @example
|
|
715
|
+
* ```typescript
|
|
716
|
+
* const { downloadUrl } = await paceful.analytics.export({
|
|
717
|
+
* type: 'user_summary',
|
|
718
|
+
* period: '30d'
|
|
719
|
+
* });
|
|
720
|
+
* ```
|
|
721
|
+
*/
|
|
722
|
+
async export(params) {
|
|
723
|
+
return this.http.post("/api/v1/partner/analytics/export", params);
|
|
724
|
+
}
|
|
725
|
+
};
|
|
726
|
+
|
|
727
|
+
// src/modules/webhooks.ts
|
|
728
|
+
var Webhooks = class {
|
|
729
|
+
constructor(http) {
|
|
730
|
+
this.http = http;
|
|
731
|
+
}
|
|
732
|
+
/**
|
|
733
|
+
* Register a webhook endpoint for receiving events
|
|
734
|
+
* Updates existing webhook if same URL already registered
|
|
735
|
+
*
|
|
736
|
+
* @param params - Webhook configuration
|
|
737
|
+
* @returns Webhook registration details including secret
|
|
738
|
+
*
|
|
739
|
+
* @example
|
|
740
|
+
* ```typescript
|
|
741
|
+
* const webhook = await paceful.webhooks.register({
|
|
742
|
+
* url: 'https://yourapp.com/webhooks/paceful',
|
|
743
|
+
* events: ['ers.stage_changed', 'mood.critical']
|
|
744
|
+
* });
|
|
745
|
+
*
|
|
746
|
+
* // Store the secret securely for signature verification
|
|
747
|
+
* console.log('Webhook secret:', webhook.secret);
|
|
748
|
+
* ```
|
|
749
|
+
*/
|
|
750
|
+
async register(params) {
|
|
751
|
+
return this.http.post("/api/v1/partner/webhooks/register", {
|
|
752
|
+
url: params.url,
|
|
753
|
+
events: params.events
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* List all registered webhooks
|
|
758
|
+
*
|
|
759
|
+
* @returns Array of webhook configurations
|
|
760
|
+
*
|
|
761
|
+
* @example
|
|
762
|
+
* ```typescript
|
|
763
|
+
* const webhooks = await paceful.webhooks.list();
|
|
764
|
+
*
|
|
765
|
+
* for (const webhook of webhooks) {
|
|
766
|
+
* console.log(`${webhook.url}: ${webhook.events.join(', ')}`);
|
|
767
|
+
* }
|
|
768
|
+
* ```
|
|
769
|
+
*/
|
|
770
|
+
async list() {
|
|
771
|
+
return this.http.get("/api/v1/partner/webhooks");
|
|
772
|
+
}
|
|
773
|
+
/**
|
|
774
|
+
* Get a specific webhook by ID
|
|
775
|
+
*
|
|
776
|
+
* @param webhookId - The webhook ID
|
|
777
|
+
* @returns Webhook configuration
|
|
778
|
+
*
|
|
779
|
+
* @example
|
|
780
|
+
* ```typescript
|
|
781
|
+
* const webhook = await paceful.webhooks.get('webhook_abc123');
|
|
782
|
+
* ```
|
|
783
|
+
*/
|
|
784
|
+
async get(webhookId) {
|
|
785
|
+
return this.http.get(`/api/v1/partner/webhooks/${webhookId}`);
|
|
786
|
+
}
|
|
787
|
+
/**
|
|
788
|
+
* Update a webhook's configuration
|
|
789
|
+
*
|
|
790
|
+
* @param webhookId - The webhook ID
|
|
791
|
+
* @param params - Updated configuration
|
|
792
|
+
* @returns Updated webhook
|
|
793
|
+
*
|
|
794
|
+
* @example
|
|
795
|
+
* ```typescript
|
|
796
|
+
* await paceful.webhooks.update('webhook_abc123', {
|
|
797
|
+
* events: ['ers.stage_changed', 'mood.critical', 'journal.created']
|
|
798
|
+
* });
|
|
799
|
+
* ```
|
|
800
|
+
*/
|
|
801
|
+
async update(webhookId, params) {
|
|
802
|
+
return this.http.patch(`/api/v1/partner/webhooks/${webhookId}`, params);
|
|
803
|
+
}
|
|
804
|
+
/**
|
|
805
|
+
* Delete a webhook
|
|
806
|
+
*
|
|
807
|
+
* @param webhookId - The webhook ID
|
|
808
|
+
*
|
|
809
|
+
* @example
|
|
810
|
+
* ```typescript
|
|
811
|
+
* await paceful.webhooks.delete('webhook_abc123');
|
|
812
|
+
* ```
|
|
813
|
+
*/
|
|
814
|
+
async delete(webhookId) {
|
|
815
|
+
await this.http.delete(`/api/v1/partner/webhooks/${webhookId}`);
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* Test a webhook by sending a test event
|
|
819
|
+
*
|
|
820
|
+
* @param webhookId - The webhook ID
|
|
821
|
+
* @returns Test result
|
|
822
|
+
*
|
|
823
|
+
* @example
|
|
824
|
+
* ```typescript
|
|
825
|
+
* const result = await paceful.webhooks.test('webhook_abc123');
|
|
826
|
+
*
|
|
827
|
+
* if (result.success) {
|
|
828
|
+
* console.log('Webhook is working!');
|
|
829
|
+
* } else {
|
|
830
|
+
* console.error('Test failed:', result.error);
|
|
831
|
+
* }
|
|
832
|
+
* ```
|
|
833
|
+
*/
|
|
834
|
+
async test(webhookId) {
|
|
835
|
+
return this.http.post(`/api/v1/partner/webhooks/${webhookId}/test`);
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* Get delivery history for a webhook
|
|
839
|
+
*
|
|
840
|
+
* @param webhookId - The webhook ID
|
|
841
|
+
* @param params - Query parameters
|
|
842
|
+
* @returns Delivery history
|
|
843
|
+
*
|
|
844
|
+
* @example
|
|
845
|
+
* ```typescript
|
|
846
|
+
* const history = await paceful.webhooks.deliveryHistory('webhook_abc123', {
|
|
847
|
+
* limit: 20
|
|
848
|
+
* });
|
|
849
|
+
*
|
|
850
|
+
* for (const delivery of history) {
|
|
851
|
+
* console.log(`${delivery.event}: ${delivery.status}`);
|
|
852
|
+
* }
|
|
853
|
+
* ```
|
|
854
|
+
*/
|
|
855
|
+
async deliveryHistory(webhookId, params) {
|
|
856
|
+
return this.http.get(`/api/v1/partner/webhooks/${webhookId}/deliveries`, {
|
|
857
|
+
limit: params?.limit,
|
|
858
|
+
status: params?.status
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
/**
|
|
862
|
+
* Rotate the webhook secret
|
|
863
|
+
*
|
|
864
|
+
* @param webhookId - The webhook ID
|
|
865
|
+
* @returns New webhook secret
|
|
866
|
+
*
|
|
867
|
+
* @example
|
|
868
|
+
* ```typescript
|
|
869
|
+
* const { secret } = await paceful.webhooks.rotateSecret('webhook_abc123');
|
|
870
|
+
*
|
|
871
|
+
* // Update your server with the new secret
|
|
872
|
+
* console.log('New secret:', secret);
|
|
873
|
+
* ```
|
|
874
|
+
*/
|
|
875
|
+
async rotateSecret(webhookId) {
|
|
876
|
+
return this.http.post(`/api/v1/partner/webhooks/${webhookId}/rotate-secret`);
|
|
877
|
+
}
|
|
878
|
+
/**
|
|
879
|
+
* Verify a webhook signature
|
|
880
|
+
* Call this method to validate incoming webhook requests
|
|
881
|
+
*
|
|
882
|
+
* @param payload - Raw request body as string
|
|
883
|
+
* @param signature - Value of X-Paceful-Signature header
|
|
884
|
+
* @param secret - Your webhook secret
|
|
885
|
+
* @returns Whether the signature is valid
|
|
886
|
+
*
|
|
887
|
+
* @example
|
|
888
|
+
* ```typescript
|
|
889
|
+
* // In your webhook handler:
|
|
890
|
+
* const isValid = Webhooks.verifySignature(
|
|
891
|
+
* rawBody,
|
|
892
|
+
* request.headers['x-paceful-signature'],
|
|
893
|
+
* process.env.PACEFUL_WEBHOOK_SECRET
|
|
894
|
+
* );
|
|
895
|
+
*
|
|
896
|
+
* if (!isValid) {
|
|
897
|
+
* return res.status(401).send('Invalid signature');
|
|
898
|
+
* }
|
|
899
|
+
* ```
|
|
900
|
+
*/
|
|
901
|
+
static verifySignature(payload, signature, secret) {
|
|
902
|
+
try {
|
|
903
|
+
const crypto = __require("crypto");
|
|
904
|
+
const expectedSignature = crypto.createHmac("sha256", secret).update(payload).digest("hex");
|
|
905
|
+
const sigBuffer = Buffer.from(signature);
|
|
906
|
+
const expectedBuffer = Buffer.from(expectedSignature);
|
|
907
|
+
if (sigBuffer.length !== expectedBuffer.length) {
|
|
908
|
+
return false;
|
|
909
|
+
}
|
|
910
|
+
return crypto.timingSafeEqual(sigBuffer, expectedBuffer);
|
|
911
|
+
} catch {
|
|
912
|
+
console.warn("Webhook signature verification requires Node.js crypto module");
|
|
913
|
+
return false;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
};
|
|
917
|
+
|
|
918
|
+
// src/client.ts
|
|
919
|
+
var PacefulClient = class {
|
|
920
|
+
/**
|
|
921
|
+
* Create a new Paceful client
|
|
922
|
+
*
|
|
923
|
+
* @param config - Configuration options
|
|
924
|
+
*
|
|
925
|
+
* @example
|
|
926
|
+
* ```typescript
|
|
927
|
+
* import { PacefulClient } from '@paceful/sdk';
|
|
928
|
+
*
|
|
929
|
+
* const paceful = new PacefulClient({
|
|
930
|
+
* apiKey: process.env.PACEFUL_API_KEY,
|
|
931
|
+
* });
|
|
932
|
+
*
|
|
933
|
+
* // Register a user
|
|
934
|
+
* const user = await paceful.users.register({
|
|
935
|
+
* externalId: 'user_123',
|
|
936
|
+
* email: 'user@example.com'
|
|
937
|
+
* });
|
|
938
|
+
*
|
|
939
|
+
* // Log a mood entry
|
|
940
|
+
* await paceful.mood.log({
|
|
941
|
+
* externalId: 'user_123',
|
|
942
|
+
* mood: 4,
|
|
943
|
+
* note: 'Feeling good today'
|
|
944
|
+
* });
|
|
945
|
+
*
|
|
946
|
+
* // Get ERS score
|
|
947
|
+
* const ers = await paceful.ers.get('user_123');
|
|
948
|
+
* console.log(`ERS: ${ers.ersScore}, Stage: ${ers.stage}`);
|
|
949
|
+
* ```
|
|
950
|
+
*/
|
|
951
|
+
constructor(config) {
|
|
952
|
+
this.http = new HttpClient(config);
|
|
953
|
+
this.users = new Users(this.http);
|
|
954
|
+
this.mood = new Mood(this.http);
|
|
955
|
+
this.journal = new Journal(this.http);
|
|
956
|
+
this.ers = new ERS(this.http);
|
|
957
|
+
this.analytics = new Analytics(this.http);
|
|
958
|
+
this.webhooks = new Webhooks(this.http);
|
|
959
|
+
}
|
|
960
|
+
/**
|
|
961
|
+
* Check API connectivity and authentication
|
|
962
|
+
*
|
|
963
|
+
* @returns Health check result
|
|
964
|
+
*
|
|
965
|
+
* @example
|
|
966
|
+
* ```typescript
|
|
967
|
+
* const health = await paceful.healthCheck();
|
|
968
|
+
* if (health.ok) {
|
|
969
|
+
* console.log('API is reachable');
|
|
970
|
+
* }
|
|
971
|
+
* ```
|
|
972
|
+
*/
|
|
973
|
+
async healthCheck() {
|
|
974
|
+
return this.http.get("/api/v1/partner/health");
|
|
975
|
+
}
|
|
976
|
+
};
|
|
977
|
+
|
|
978
|
+
// src/widgets/index.ts
|
|
979
|
+
init_PacefulProvider();
|
|
980
|
+
|
|
981
|
+
// src/widgets/MoodWidget.tsx
|
|
982
|
+
import { useState, useEffect } from "react";
|
|
983
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
984
|
+
var MOOD_OPTIONS = [
|
|
985
|
+
{ value: 1, label: "Struggling", color: "#7E71B5", subtitle: "Having a hard time" },
|
|
986
|
+
{ value: 2, label: "Low", color: "#5B7FB5", subtitle: "Feeling down" },
|
|
987
|
+
{ value: 3, label: "Okay", color: "#C4973B", subtitle: "Getting by" },
|
|
988
|
+
{ value: 4, label: "Good", color: "#5B8A72", subtitle: "Feeling positive" },
|
|
989
|
+
{ value: 5, label: "Great", color: "#3D6B54", subtitle: "Thriving today" }
|
|
990
|
+
];
|
|
991
|
+
var EMOTION_TAGS = [
|
|
992
|
+
"anxious",
|
|
993
|
+
"sad",
|
|
994
|
+
"lonely",
|
|
995
|
+
"angry",
|
|
996
|
+
"numb",
|
|
997
|
+
"hopeful",
|
|
998
|
+
"grateful",
|
|
999
|
+
"calm",
|
|
1000
|
+
"motivated",
|
|
1001
|
+
"confused"
|
|
1002
|
+
];
|
|
1003
|
+
function MoodWidget({
|
|
1004
|
+
apiKey: propApiKey,
|
|
1005
|
+
userId: propUserId,
|
|
1006
|
+
baseUrl: propBaseUrl,
|
|
1007
|
+
theme = "light",
|
|
1008
|
+
brandColor = "#5B8A72",
|
|
1009
|
+
compact = false,
|
|
1010
|
+
showStreak = false,
|
|
1011
|
+
onComplete,
|
|
1012
|
+
onError,
|
|
1013
|
+
className
|
|
1014
|
+
}) {
|
|
1015
|
+
let contextValue = null;
|
|
1016
|
+
try {
|
|
1017
|
+
const { usePaceful: usePaceful2 } = (init_PacefulProvider(), __toCommonJS(PacefulProvider_exports));
|
|
1018
|
+
contextValue = usePaceful2();
|
|
1019
|
+
} catch {
|
|
1020
|
+
}
|
|
1021
|
+
const apiKey = propApiKey || contextValue?.apiKey;
|
|
1022
|
+
const userId = propUserId || contextValue?.userId;
|
|
1023
|
+
const baseUrl = propBaseUrl || contextValue?.baseUrl || "https://paceful-app.vercel.app/api/v1/partner";
|
|
1024
|
+
const [state, setState] = useState("INITIAL");
|
|
1025
|
+
const [selectedMood, setSelectedMood] = useState(null);
|
|
1026
|
+
const [selectedEmotions, setSelectedEmotions] = useState([]);
|
|
1027
|
+
const [streak, setStreak] = useState(0);
|
|
1028
|
+
const isDark = theme === "dark";
|
|
1029
|
+
useEffect(() => {
|
|
1030
|
+
if (showStreak && apiKey && userId && typeof window !== "undefined") {
|
|
1031
|
+
fetch(`${baseUrl}/mood/${userId}/streak`, {
|
|
1032
|
+
headers: { "X-API-Key": apiKey }
|
|
1033
|
+
}).then((res) => res.json()).then((data) => {
|
|
1034
|
+
if (data.success && data.data?.streak) {
|
|
1035
|
+
setStreak(data.data.streak);
|
|
1036
|
+
}
|
|
1037
|
+
}).catch(() => {
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
1040
|
+
}, [showStreak, apiKey, userId, baseUrl]);
|
|
1041
|
+
const handleMoodSelect = (mood) => {
|
|
1042
|
+
setSelectedMood(mood);
|
|
1043
|
+
if (compact) {
|
|
1044
|
+
handleSubmit(mood, []);
|
|
1045
|
+
} else {
|
|
1046
|
+
setState("EMOTIONS");
|
|
1047
|
+
}
|
|
1048
|
+
};
|
|
1049
|
+
const toggleEmotion = (emotion) => {
|
|
1050
|
+
setSelectedEmotions(
|
|
1051
|
+
(prev) => prev.includes(emotion) ? prev.filter((e) => e !== emotion) : [...prev, emotion]
|
|
1052
|
+
);
|
|
1053
|
+
};
|
|
1054
|
+
const handleSubmit = async (mood, emotions) => {
|
|
1055
|
+
const moodToSubmit = mood ?? selectedMood;
|
|
1056
|
+
const emotionsToSubmit = emotions ?? selectedEmotions;
|
|
1057
|
+
if (!moodToSubmit || !apiKey || !userId) {
|
|
1058
|
+
onError?.(new Error("Missing required data"));
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
1061
|
+
setState("SUBMITTING");
|
|
1062
|
+
try {
|
|
1063
|
+
const moodOption = MOOD_OPTIONS.find((m) => m.value === moodToSubmit);
|
|
1064
|
+
const response = await fetch(`${baseUrl}/mood/log`, {
|
|
1065
|
+
method: "POST",
|
|
1066
|
+
headers: {
|
|
1067
|
+
"Content-Type": "application/json",
|
|
1068
|
+
"X-API-Key": apiKey
|
|
1069
|
+
},
|
|
1070
|
+
body: JSON.stringify({
|
|
1071
|
+
externalId: userId,
|
|
1072
|
+
score: moodToSubmit,
|
|
1073
|
+
label: moodOption?.label,
|
|
1074
|
+
emotions: emotionsToSubmit
|
|
1075
|
+
})
|
|
1076
|
+
});
|
|
1077
|
+
const data = await response.json();
|
|
1078
|
+
if (!response.ok) {
|
|
1079
|
+
throw new Error(data.error?.message || "Failed to log mood");
|
|
1080
|
+
}
|
|
1081
|
+
setState("COMPLETE");
|
|
1082
|
+
onComplete?.({
|
|
1083
|
+
logged: true,
|
|
1084
|
+
moodId: data.data?.id || "",
|
|
1085
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1086
|
+
});
|
|
1087
|
+
setTimeout(() => {
|
|
1088
|
+
setState("INITIAL");
|
|
1089
|
+
setSelectedMood(null);
|
|
1090
|
+
setSelectedEmotions([]);
|
|
1091
|
+
if (showStreak) setStreak((s) => s + 1);
|
|
1092
|
+
}, 2e3);
|
|
1093
|
+
} catch (error) {
|
|
1094
|
+
setState("INITIAL");
|
|
1095
|
+
onError?.(error instanceof Error ? error : new Error("Unknown error"));
|
|
1096
|
+
}
|
|
1097
|
+
};
|
|
1098
|
+
const styles = {
|
|
1099
|
+
container: {
|
|
1100
|
+
backgroundColor: isDark ? "#1F1D1A" : "#FFFFFF",
|
|
1101
|
+
borderRadius: 12,
|
|
1102
|
+
padding: 20,
|
|
1103
|
+
fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
1104
|
+
color: isDark ? "#FFFFFF" : "#1F1D1A"
|
|
1105
|
+
},
|
|
1106
|
+
title: {
|
|
1107
|
+
fontSize: 18,
|
|
1108
|
+
fontWeight: 600,
|
|
1109
|
+
marginBottom: 16,
|
|
1110
|
+
textAlign: "center"
|
|
1111
|
+
},
|
|
1112
|
+
moodList: {
|
|
1113
|
+
display: "flex",
|
|
1114
|
+
flexDirection: "column",
|
|
1115
|
+
gap: 8
|
|
1116
|
+
},
|
|
1117
|
+
moodCard: (isSelected) => ({
|
|
1118
|
+
display: "flex",
|
|
1119
|
+
alignItems: "center",
|
|
1120
|
+
gap: 12,
|
|
1121
|
+
border: `1px solid ${isSelected ? brandColor : isDark ? "#3D3D3A" : "#E5E0D9"}`,
|
|
1122
|
+
borderRadius: 8,
|
|
1123
|
+
padding: 12,
|
|
1124
|
+
cursor: "pointer",
|
|
1125
|
+
backgroundColor: isSelected ? isDark ? "#2D2D2A" : "#F9F6F2" : "transparent",
|
|
1126
|
+
transition: "all 0.15s ease"
|
|
1127
|
+
}),
|
|
1128
|
+
moodDot: (color) => ({
|
|
1129
|
+
width: 12,
|
|
1130
|
+
height: 12,
|
|
1131
|
+
borderRadius: "50%",
|
|
1132
|
+
backgroundColor: color,
|
|
1133
|
+
flexShrink: 0
|
|
1134
|
+
}),
|
|
1135
|
+
moodLabel: {
|
|
1136
|
+
fontWeight: 500,
|
|
1137
|
+
fontSize: 15
|
|
1138
|
+
},
|
|
1139
|
+
moodSubtitle: {
|
|
1140
|
+
fontSize: 13,
|
|
1141
|
+
color: isDark ? "#9A938A" : "#6B6560",
|
|
1142
|
+
marginLeft: "auto"
|
|
1143
|
+
},
|
|
1144
|
+
emotionsSection: {
|
|
1145
|
+
marginTop: 20
|
|
1146
|
+
},
|
|
1147
|
+
emotionsTitle: {
|
|
1148
|
+
fontSize: 14,
|
|
1149
|
+
color: isDark ? "#9A938A" : "#6B6560",
|
|
1150
|
+
marginBottom: 12
|
|
1151
|
+
},
|
|
1152
|
+
emotionTags: {
|
|
1153
|
+
display: "flex",
|
|
1154
|
+
flexWrap: "wrap",
|
|
1155
|
+
gap: 8
|
|
1156
|
+
},
|
|
1157
|
+
emotionTag: (isSelected) => ({
|
|
1158
|
+
border: `1px solid ${isSelected ? brandColor : isDark ? "#3D3D3A" : "#E5E0D9"}`,
|
|
1159
|
+
borderRadius: 20,
|
|
1160
|
+
padding: "6px 12px",
|
|
1161
|
+
fontSize: 13,
|
|
1162
|
+
cursor: "pointer",
|
|
1163
|
+
backgroundColor: isSelected ? brandColor : "transparent",
|
|
1164
|
+
color: isSelected ? "#FFFFFF" : isDark ? "#FFFFFF" : "#1F1D1A",
|
|
1165
|
+
transition: "all 0.15s ease"
|
|
1166
|
+
}),
|
|
1167
|
+
submitButton: {
|
|
1168
|
+
marginTop: 20,
|
|
1169
|
+
width: "100%",
|
|
1170
|
+
backgroundColor: brandColor,
|
|
1171
|
+
color: "#FFFFFF",
|
|
1172
|
+
border: "none",
|
|
1173
|
+
borderRadius: 8,
|
|
1174
|
+
padding: 12,
|
|
1175
|
+
fontSize: 15,
|
|
1176
|
+
fontWeight: 500,
|
|
1177
|
+
cursor: "pointer"
|
|
1178
|
+
},
|
|
1179
|
+
footer: {
|
|
1180
|
+
marginTop: 16,
|
|
1181
|
+
textAlign: "center",
|
|
1182
|
+
fontSize: 11,
|
|
1183
|
+
color: "#9A938A"
|
|
1184
|
+
},
|
|
1185
|
+
spinner: {
|
|
1186
|
+
display: "flex",
|
|
1187
|
+
justifyContent: "center",
|
|
1188
|
+
alignItems: "center",
|
|
1189
|
+
padding: 40
|
|
1190
|
+
},
|
|
1191
|
+
checkmark: {
|
|
1192
|
+
display: "flex",
|
|
1193
|
+
flexDirection: "column",
|
|
1194
|
+
alignItems: "center",
|
|
1195
|
+
padding: 40,
|
|
1196
|
+
gap: 12
|
|
1197
|
+
},
|
|
1198
|
+
streakBadge: {
|
|
1199
|
+
display: "inline-flex",
|
|
1200
|
+
alignItems: "center",
|
|
1201
|
+
gap: 4,
|
|
1202
|
+
backgroundColor: isDark ? "#2D2D2A" : "#F9F6F2",
|
|
1203
|
+
borderRadius: 12,
|
|
1204
|
+
padding: "4px 10px",
|
|
1205
|
+
fontSize: 12,
|
|
1206
|
+
color: brandColor,
|
|
1207
|
+
marginBottom: 16
|
|
1208
|
+
}
|
|
1209
|
+
};
|
|
1210
|
+
if (state === "SUBMITTING") {
|
|
1211
|
+
return /* @__PURE__ */ jsxs("div", { style: styles.container, className, children: [
|
|
1212
|
+
/* @__PURE__ */ jsx("div", { style: styles.spinner, children: /* @__PURE__ */ jsx("svg", { width: "32", height: "32", viewBox: "0 0 32 32", children: /* @__PURE__ */ jsx(
|
|
1213
|
+
"circle",
|
|
1214
|
+
{
|
|
1215
|
+
cx: "16",
|
|
1216
|
+
cy: "16",
|
|
1217
|
+
r: "12",
|
|
1218
|
+
stroke: brandColor,
|
|
1219
|
+
strokeWidth: "3",
|
|
1220
|
+
fill: "none",
|
|
1221
|
+
strokeDasharray: "60",
|
|
1222
|
+
strokeLinecap: "round",
|
|
1223
|
+
children: /* @__PURE__ */ jsx(
|
|
1224
|
+
"animateTransform",
|
|
1225
|
+
{
|
|
1226
|
+
attributeName: "transform",
|
|
1227
|
+
type: "rotate",
|
|
1228
|
+
from: "0 16 16",
|
|
1229
|
+
to: "360 16 16",
|
|
1230
|
+
dur: "1s",
|
|
1231
|
+
repeatCount: "indefinite"
|
|
1232
|
+
}
|
|
1233
|
+
)
|
|
1234
|
+
}
|
|
1235
|
+
) }) }),
|
|
1236
|
+
/* @__PURE__ */ jsx("div", { style: styles.footer, children: "Powered by Paceful" })
|
|
1237
|
+
] });
|
|
1238
|
+
}
|
|
1239
|
+
if (state === "COMPLETE") {
|
|
1240
|
+
return /* @__PURE__ */ jsxs("div", { style: styles.container, className, children: [
|
|
1241
|
+
/* @__PURE__ */ jsxs("div", { style: styles.checkmark, children: [
|
|
1242
|
+
/* @__PURE__ */ jsxs("svg", { width: "48", height: "48", viewBox: "0 0 48 48", children: [
|
|
1243
|
+
/* @__PURE__ */ jsx("circle", { cx: "24", cy: "24", r: "22", fill: brandColor }),
|
|
1244
|
+
/* @__PURE__ */ jsx(
|
|
1245
|
+
"path",
|
|
1246
|
+
{
|
|
1247
|
+
d: "M14 24 L21 31 L34 18",
|
|
1248
|
+
stroke: "#FFFFFF",
|
|
1249
|
+
strokeWidth: "3",
|
|
1250
|
+
fill: "none",
|
|
1251
|
+
strokeLinecap: "round",
|
|
1252
|
+
strokeLinejoin: "round"
|
|
1253
|
+
}
|
|
1254
|
+
)
|
|
1255
|
+
] }),
|
|
1256
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: 18, fontWeight: 500 }, children: "Logged!" })
|
|
1257
|
+
] }),
|
|
1258
|
+
/* @__PURE__ */ jsx("div", { style: styles.footer, children: "Powered by Paceful" })
|
|
1259
|
+
] });
|
|
1260
|
+
}
|
|
1261
|
+
if (state === "EMOTIONS") {
|
|
1262
|
+
const selectedMoodOption = MOOD_OPTIONS.find((m) => m.value === selectedMood);
|
|
1263
|
+
return /* @__PURE__ */ jsxs("div", { style: styles.container, className, children: [
|
|
1264
|
+
/* @__PURE__ */ jsxs("div", { style: styles.title, children: [
|
|
1265
|
+
/* @__PURE__ */ jsx("span", { style: { color: selectedMoodOption?.color }, children: selectedMoodOption?.label }),
|
|
1266
|
+
" ",
|
|
1267
|
+
"\u2014 What emotions are present?"
|
|
1268
|
+
] }),
|
|
1269
|
+
/* @__PURE__ */ jsx("div", { style: styles.emotionTags, children: EMOTION_TAGS.map((emotion) => /* @__PURE__ */ jsx(
|
|
1270
|
+
"button",
|
|
1271
|
+
{
|
|
1272
|
+
onClick: () => toggleEmotion(emotion),
|
|
1273
|
+
style: styles.emotionTag(selectedEmotions.includes(emotion)),
|
|
1274
|
+
children: emotion
|
|
1275
|
+
},
|
|
1276
|
+
emotion
|
|
1277
|
+
)) }),
|
|
1278
|
+
/* @__PURE__ */ jsx(
|
|
1279
|
+
"button",
|
|
1280
|
+
{
|
|
1281
|
+
onClick: () => handleSubmit(),
|
|
1282
|
+
style: styles.submitButton,
|
|
1283
|
+
children: "Log Mood"
|
|
1284
|
+
}
|
|
1285
|
+
),
|
|
1286
|
+
/* @__PURE__ */ jsx("div", { style: styles.footer, children: "Powered by Paceful" })
|
|
1287
|
+
] });
|
|
1288
|
+
}
|
|
1289
|
+
return /* @__PURE__ */ jsxs("div", { style: styles.container, className, children: [
|
|
1290
|
+
showStreak && streak > 0 && /* @__PURE__ */ jsx("div", { style: { textAlign: "center" }, children: /* @__PURE__ */ jsxs("span", { style: styles.streakBadge, children: [
|
|
1291
|
+
"\u{1F525} ",
|
|
1292
|
+
streak,
|
|
1293
|
+
" day streak"
|
|
1294
|
+
] }) }),
|
|
1295
|
+
/* @__PURE__ */ jsx("div", { style: styles.title, children: "How are you feeling?" }),
|
|
1296
|
+
/* @__PURE__ */ jsx("div", { style: styles.moodList, children: MOOD_OPTIONS.map((mood) => /* @__PURE__ */ jsxs(
|
|
1297
|
+
"div",
|
|
1298
|
+
{
|
|
1299
|
+
onClick: () => handleMoodSelect(mood.value),
|
|
1300
|
+
style: styles.moodCard(selectedMood === mood.value),
|
|
1301
|
+
children: [
|
|
1302
|
+
/* @__PURE__ */ jsx("div", { style: styles.moodDot(mood.color) }),
|
|
1303
|
+
/* @__PURE__ */ jsx("span", { style: styles.moodLabel, children: mood.label }),
|
|
1304
|
+
/* @__PURE__ */ jsx("span", { style: styles.moodSubtitle, children: mood.subtitle })
|
|
1305
|
+
]
|
|
1306
|
+
},
|
|
1307
|
+
mood.value
|
|
1308
|
+
)) }),
|
|
1309
|
+
/* @__PURE__ */ jsx("div", { style: styles.footer, children: "Powered by Paceful" })
|
|
1310
|
+
] });
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
// src/widgets/JournalWidget.tsx
|
|
1314
|
+
import { useState as useState2, useEffect as useEffect2 } from "react";
|
|
1315
|
+
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1316
|
+
var JOURNAL_PROMPTS = [
|
|
1317
|
+
"What would you tell yourself from a year ago?",
|
|
1318
|
+
"What's one thing you're grateful for right now?",
|
|
1319
|
+
"What emotion keeps coming back today?",
|
|
1320
|
+
"What did you learn about yourself this week?",
|
|
1321
|
+
"If your future self could see you now, what would they say?"
|
|
1322
|
+
];
|
|
1323
|
+
var SENTIMENT_COLORS = {
|
|
1324
|
+
Hopeful: "#5B8A72",
|
|
1325
|
+
Reflective: "#5B7FB5",
|
|
1326
|
+
Processing: "#C4973B",
|
|
1327
|
+
Struggling: "#B56B6B"
|
|
1328
|
+
};
|
|
1329
|
+
function JournalWidget({
|
|
1330
|
+
apiKey: propApiKey,
|
|
1331
|
+
userId: propUserId,
|
|
1332
|
+
baseUrl: propBaseUrl,
|
|
1333
|
+
theme = "light",
|
|
1334
|
+
brandColor = "#5B8A72",
|
|
1335
|
+
showPrompt = true,
|
|
1336
|
+
showAIReflection = true,
|
|
1337
|
+
maxLength = 2e3,
|
|
1338
|
+
placeholder = "What's on your mind?",
|
|
1339
|
+
onComplete,
|
|
1340
|
+
onError,
|
|
1341
|
+
className
|
|
1342
|
+
}) {
|
|
1343
|
+
let contextValue = null;
|
|
1344
|
+
try {
|
|
1345
|
+
const { usePaceful: usePaceful2 } = (init_PacefulProvider(), __toCommonJS(PacefulProvider_exports));
|
|
1346
|
+
contextValue = usePaceful2();
|
|
1347
|
+
} catch {
|
|
1348
|
+
}
|
|
1349
|
+
const apiKey = propApiKey || contextValue?.apiKey;
|
|
1350
|
+
const userId = propUserId || contextValue?.userId;
|
|
1351
|
+
const baseUrl = propBaseUrl || contextValue?.baseUrl || "https://paceful-app.vercel.app/api/v1/partner";
|
|
1352
|
+
const [state, setState] = useState2("WRITING");
|
|
1353
|
+
const [content, setContent] = useState2("");
|
|
1354
|
+
const [prompt, setPrompt] = useState2("");
|
|
1355
|
+
const [result, setResult] = useState2(null);
|
|
1356
|
+
const [fadeIn, setFadeIn] = useState2(false);
|
|
1357
|
+
const isDark = theme === "dark";
|
|
1358
|
+
useEffect2(() => {
|
|
1359
|
+
if (showPrompt && typeof window !== "undefined") {
|
|
1360
|
+
const randomIndex = Math.floor(Math.random() * JOURNAL_PROMPTS.length);
|
|
1361
|
+
setPrompt(JOURNAL_PROMPTS[randomIndex]);
|
|
1362
|
+
}
|
|
1363
|
+
}, [showPrompt]);
|
|
1364
|
+
const wordCount = content.trim().split(/\s+/).filter(Boolean).length;
|
|
1365
|
+
const handleSubmit = async () => {
|
|
1366
|
+
if (!content.trim() || !apiKey || !userId) {
|
|
1367
|
+
onError?.(new Error("Missing required data"));
|
|
1368
|
+
return;
|
|
1369
|
+
}
|
|
1370
|
+
setState("SUBMITTING");
|
|
1371
|
+
try {
|
|
1372
|
+
const response = await fetch(`${baseUrl}/journal/entry`, {
|
|
1373
|
+
method: "POST",
|
|
1374
|
+
headers: {
|
|
1375
|
+
"Content-Type": "application/json",
|
|
1376
|
+
"X-API-Key": apiKey
|
|
1377
|
+
},
|
|
1378
|
+
body: JSON.stringify({
|
|
1379
|
+
externalId: userId,
|
|
1380
|
+
content: content.trim()
|
|
1381
|
+
})
|
|
1382
|
+
});
|
|
1383
|
+
const data = await response.json();
|
|
1384
|
+
if (!response.ok) {
|
|
1385
|
+
throw new Error(data.error?.message || "Failed to save journal entry");
|
|
1386
|
+
}
|
|
1387
|
+
const entryResult = {
|
|
1388
|
+
entryId: data.data?.id || "",
|
|
1389
|
+
sentiment: data.data?.sentiment || "Reflective",
|
|
1390
|
+
aiReflection: data.data?.aiReflection || "Thank you for sharing. Taking time to reflect is an important step in understanding yourself better."
|
|
1391
|
+
};
|
|
1392
|
+
setResult(entryResult);
|
|
1393
|
+
setState("COMPLETE");
|
|
1394
|
+
onComplete?.(entryResult);
|
|
1395
|
+
setTimeout(() => setFadeIn(true), 50);
|
|
1396
|
+
} catch (error) {
|
|
1397
|
+
setState("WRITING");
|
|
1398
|
+
onError?.(error instanceof Error ? error : new Error("Unknown error"));
|
|
1399
|
+
}
|
|
1400
|
+
};
|
|
1401
|
+
const handleWriteAnother = () => {
|
|
1402
|
+
setState("WRITING");
|
|
1403
|
+
setContent("");
|
|
1404
|
+
setResult(null);
|
|
1405
|
+
setFadeIn(false);
|
|
1406
|
+
if (showPrompt) {
|
|
1407
|
+
const randomIndex = Math.floor(Math.random() * JOURNAL_PROMPTS.length);
|
|
1408
|
+
setPrompt(JOURNAL_PROMPTS[randomIndex]);
|
|
1409
|
+
}
|
|
1410
|
+
};
|
|
1411
|
+
const styles = {
|
|
1412
|
+
container: {
|
|
1413
|
+
backgroundColor: isDark ? "#1F1D1A" : "#FFFFFF",
|
|
1414
|
+
borderRadius: 12,
|
|
1415
|
+
padding: 20,
|
|
1416
|
+
fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
1417
|
+
color: isDark ? "#FFFFFF" : "#1F1D1A"
|
|
1418
|
+
},
|
|
1419
|
+
prompt: {
|
|
1420
|
+
fontSize: 16,
|
|
1421
|
+
fontWeight: 500,
|
|
1422
|
+
marginBottom: 16,
|
|
1423
|
+
color: isDark ? "#E5E0D9" : "#3D3D3A",
|
|
1424
|
+
lineHeight: 1.4
|
|
1425
|
+
},
|
|
1426
|
+
textareaWrapper: {
|
|
1427
|
+
position: "relative"
|
|
1428
|
+
},
|
|
1429
|
+
textarea: {
|
|
1430
|
+
width: "100%",
|
|
1431
|
+
minHeight: 120,
|
|
1432
|
+
resize: "vertical",
|
|
1433
|
+
border: `1px solid ${isDark ? "#3D3D3A" : "#E5E0D9"}`,
|
|
1434
|
+
borderRadius: 8,
|
|
1435
|
+
padding: 12,
|
|
1436
|
+
fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
1437
|
+
fontSize: 15,
|
|
1438
|
+
lineHeight: 1.5,
|
|
1439
|
+
backgroundColor: isDark ? "#2D2D2A" : "#FFFFFF",
|
|
1440
|
+
color: isDark ? "#FFFFFF" : "#1F1D1A",
|
|
1441
|
+
outline: "none",
|
|
1442
|
+
boxSizing: "border-box"
|
|
1443
|
+
},
|
|
1444
|
+
wordCount: {
|
|
1445
|
+
position: "absolute",
|
|
1446
|
+
bottom: 8,
|
|
1447
|
+
right: 12,
|
|
1448
|
+
fontSize: 12,
|
|
1449
|
+
color: "#9A938A"
|
|
1450
|
+
},
|
|
1451
|
+
submitButton: {
|
|
1452
|
+
marginTop: 16,
|
|
1453
|
+
width: "100%",
|
|
1454
|
+
backgroundColor: brandColor,
|
|
1455
|
+
color: "#FFFFFF",
|
|
1456
|
+
border: "none",
|
|
1457
|
+
borderRadius: 8,
|
|
1458
|
+
padding: 12,
|
|
1459
|
+
fontSize: 15,
|
|
1460
|
+
fontWeight: 500,
|
|
1461
|
+
cursor: content.trim() ? "pointer" : "not-allowed",
|
|
1462
|
+
opacity: content.trim() ? 1 : 0.5
|
|
1463
|
+
},
|
|
1464
|
+
footer: {
|
|
1465
|
+
marginTop: 16,
|
|
1466
|
+
textAlign: "center",
|
|
1467
|
+
fontSize: 11,
|
|
1468
|
+
color: "#9A938A"
|
|
1469
|
+
},
|
|
1470
|
+
submittingText: {
|
|
1471
|
+
textAlign: "center",
|
|
1472
|
+
padding: 40,
|
|
1473
|
+
fontSize: 15,
|
|
1474
|
+
color: isDark ? "#9A938A" : "#6B6560"
|
|
1475
|
+
},
|
|
1476
|
+
reflectionContainer: {
|
|
1477
|
+
opacity: fadeIn ? 1 : 0,
|
|
1478
|
+
transition: "opacity 0.3s ease"
|
|
1479
|
+
},
|
|
1480
|
+
sentimentBadge: (sentiment) => ({
|
|
1481
|
+
display: "inline-block",
|
|
1482
|
+
padding: "4px 10px",
|
|
1483
|
+
borderRadius: 12,
|
|
1484
|
+
fontSize: 12,
|
|
1485
|
+
fontWeight: 500,
|
|
1486
|
+
backgroundColor: SENTIMENT_COLORS[sentiment] || SENTIMENT_COLORS.Reflective,
|
|
1487
|
+
color: "#FFFFFF",
|
|
1488
|
+
marginBottom: 12
|
|
1489
|
+
}),
|
|
1490
|
+
reflectionBox: {
|
|
1491
|
+
borderLeft: `3px solid ${brandColor}`,
|
|
1492
|
+
padding: 16,
|
|
1493
|
+
backgroundColor: isDark ? "#2D2D2A" : "#F9F6F2",
|
|
1494
|
+
borderRadius: "0 8px 8px 0"
|
|
1495
|
+
},
|
|
1496
|
+
reflectionText: {
|
|
1497
|
+
fontStyle: "italic",
|
|
1498
|
+
lineHeight: 1.6,
|
|
1499
|
+
fontSize: 15,
|
|
1500
|
+
color: isDark ? "#E5E0D9" : "#3D3D3A"
|
|
1501
|
+
},
|
|
1502
|
+
writeAnotherButton: {
|
|
1503
|
+
marginTop: 20,
|
|
1504
|
+
width: "100%",
|
|
1505
|
+
backgroundColor: "transparent",
|
|
1506
|
+
color: brandColor,
|
|
1507
|
+
border: `1px solid ${brandColor}`,
|
|
1508
|
+
borderRadius: 8,
|
|
1509
|
+
padding: 12,
|
|
1510
|
+
fontSize: 15,
|
|
1511
|
+
fontWeight: 500,
|
|
1512
|
+
cursor: "pointer"
|
|
1513
|
+
}
|
|
1514
|
+
};
|
|
1515
|
+
if (state === "SUBMITTING") {
|
|
1516
|
+
return /* @__PURE__ */ jsxs2("div", { style: styles.container, className, children: [
|
|
1517
|
+
/* @__PURE__ */ jsxs2("div", { style: styles.submittingText, children: [
|
|
1518
|
+
/* @__PURE__ */ jsx2(
|
|
1519
|
+
"svg",
|
|
1520
|
+
{
|
|
1521
|
+
width: "32",
|
|
1522
|
+
height: "32",
|
|
1523
|
+
viewBox: "0 0 32 32",
|
|
1524
|
+
style: { display: "block", margin: "0 auto 16px" },
|
|
1525
|
+
children: /* @__PURE__ */ jsx2(
|
|
1526
|
+
"circle",
|
|
1527
|
+
{
|
|
1528
|
+
cx: "16",
|
|
1529
|
+
cy: "16",
|
|
1530
|
+
r: "12",
|
|
1531
|
+
stroke: brandColor,
|
|
1532
|
+
strokeWidth: "3",
|
|
1533
|
+
fill: "none",
|
|
1534
|
+
strokeDasharray: "60",
|
|
1535
|
+
strokeLinecap: "round",
|
|
1536
|
+
children: /* @__PURE__ */ jsx2(
|
|
1537
|
+
"animateTransform",
|
|
1538
|
+
{
|
|
1539
|
+
attributeName: "transform",
|
|
1540
|
+
type: "rotate",
|
|
1541
|
+
from: "0 16 16",
|
|
1542
|
+
to: "360 16 16",
|
|
1543
|
+
dur: "1s",
|
|
1544
|
+
repeatCount: "indefinite"
|
|
1545
|
+
}
|
|
1546
|
+
)
|
|
1547
|
+
}
|
|
1548
|
+
)
|
|
1549
|
+
}
|
|
1550
|
+
),
|
|
1551
|
+
"Reflecting..."
|
|
1552
|
+
] }),
|
|
1553
|
+
/* @__PURE__ */ jsx2("div", { style: styles.footer, children: "Powered by Paceful" })
|
|
1554
|
+
] });
|
|
1555
|
+
}
|
|
1556
|
+
if (state === "COMPLETE" && result) {
|
|
1557
|
+
return /* @__PURE__ */ jsxs2("div", { style: styles.container, className, children: [
|
|
1558
|
+
/* @__PURE__ */ jsxs2("div", { style: styles.reflectionContainer, children: [
|
|
1559
|
+
showAIReflection && /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
1560
|
+
/* @__PURE__ */ jsx2("span", { style: styles.sentimentBadge(result.sentiment), children: result.sentiment }),
|
|
1561
|
+
/* @__PURE__ */ jsx2("div", { style: styles.reflectionBox, children: /* @__PURE__ */ jsx2("p", { style: styles.reflectionText, children: result.aiReflection }) })
|
|
1562
|
+
] }),
|
|
1563
|
+
!showAIReflection && /* @__PURE__ */ jsxs2("div", { style: { textAlign: "center", padding: 20 }, children: [
|
|
1564
|
+
/* @__PURE__ */ jsxs2("svg", { width: "48", height: "48", viewBox: "0 0 48 48", children: [
|
|
1565
|
+
/* @__PURE__ */ jsx2("circle", { cx: "24", cy: "24", r: "22", fill: brandColor }),
|
|
1566
|
+
/* @__PURE__ */ jsx2(
|
|
1567
|
+
"path",
|
|
1568
|
+
{
|
|
1569
|
+
d: "M14 24 L21 31 L34 18",
|
|
1570
|
+
stroke: "#FFFFFF",
|
|
1571
|
+
strokeWidth: "3",
|
|
1572
|
+
fill: "none",
|
|
1573
|
+
strokeLinecap: "round",
|
|
1574
|
+
strokeLinejoin: "round"
|
|
1575
|
+
}
|
|
1576
|
+
)
|
|
1577
|
+
] }),
|
|
1578
|
+
/* @__PURE__ */ jsx2("p", { style: { marginTop: 12, fontWeight: 500 }, children: "Entry Saved" })
|
|
1579
|
+
] }),
|
|
1580
|
+
/* @__PURE__ */ jsx2("button", { onClick: handleWriteAnother, style: styles.writeAnotherButton, children: "Write Another" })
|
|
1581
|
+
] }),
|
|
1582
|
+
/* @__PURE__ */ jsx2("div", { style: styles.footer, children: "Powered by Paceful" })
|
|
1583
|
+
] });
|
|
1584
|
+
}
|
|
1585
|
+
return /* @__PURE__ */ jsxs2("div", { style: styles.container, className, children: [
|
|
1586
|
+
showPrompt && prompt && /* @__PURE__ */ jsx2("div", { style: styles.prompt, children: prompt }),
|
|
1587
|
+
/* @__PURE__ */ jsxs2("div", { style: styles.textareaWrapper, children: [
|
|
1588
|
+
/* @__PURE__ */ jsx2(
|
|
1589
|
+
"textarea",
|
|
1590
|
+
{
|
|
1591
|
+
value: content,
|
|
1592
|
+
onChange: (e) => setContent(e.target.value.slice(0, maxLength)),
|
|
1593
|
+
placeholder,
|
|
1594
|
+
style: styles.textarea
|
|
1595
|
+
}
|
|
1596
|
+
),
|
|
1597
|
+
/* @__PURE__ */ jsxs2("span", { style: styles.wordCount, children: [
|
|
1598
|
+
wordCount,
|
|
1599
|
+
" words"
|
|
1600
|
+
] })
|
|
1601
|
+
] }),
|
|
1602
|
+
/* @__PURE__ */ jsx2(
|
|
1603
|
+
"button",
|
|
1604
|
+
{
|
|
1605
|
+
onClick: handleSubmit,
|
|
1606
|
+
disabled: !content.trim(),
|
|
1607
|
+
style: styles.submitButton,
|
|
1608
|
+
children: "Save Entry"
|
|
1609
|
+
}
|
|
1610
|
+
),
|
|
1611
|
+
/* @__PURE__ */ jsx2("div", { style: styles.footer, children: "Powered by Paceful" })
|
|
1612
|
+
] });
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
// src/widgets/ERSDisplay.tsx
|
|
1616
|
+
import { useState as useState3, useEffect as useEffect3 } from "react";
|
|
1617
|
+
import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1618
|
+
var STAGE_COLORS = {
|
|
1619
|
+
healing: "#7E71B5",
|
|
1620
|
+
rebuilding: "#5B8A72",
|
|
1621
|
+
ready: "#D4A645"
|
|
1622
|
+
};
|
|
1623
|
+
var DIMENSION_LABELS = [
|
|
1624
|
+
{ key: "emotionalStability", label: "Emotional Stability" },
|
|
1625
|
+
{ key: "selfAwareness", label: "Self Reflection" },
|
|
1626
|
+
{ key: "socialConnection", label: "Engagement" },
|
|
1627
|
+
{ key: "purposeClarity", label: "Coping Capacity" },
|
|
1628
|
+
{ key: "resilienceGrowth", label: "Social Readiness" }
|
|
1629
|
+
];
|
|
1630
|
+
function ERSDisplay({
|
|
1631
|
+
apiKey: propApiKey,
|
|
1632
|
+
userId: propUserId,
|
|
1633
|
+
baseUrl: propBaseUrl,
|
|
1634
|
+
theme = "light",
|
|
1635
|
+
brandColor = "#5B8A72",
|
|
1636
|
+
showDimensions = true,
|
|
1637
|
+
showTrend = true,
|
|
1638
|
+
compact = false,
|
|
1639
|
+
onLoad,
|
|
1640
|
+
onError,
|
|
1641
|
+
className
|
|
1642
|
+
}) {
|
|
1643
|
+
let contextValue = null;
|
|
1644
|
+
try {
|
|
1645
|
+
const { usePaceful: usePaceful2 } = (init_PacefulProvider(), __toCommonJS(PacefulProvider_exports));
|
|
1646
|
+
contextValue = usePaceful2();
|
|
1647
|
+
} catch {
|
|
1648
|
+
}
|
|
1649
|
+
const apiKey = propApiKey || contextValue?.apiKey;
|
|
1650
|
+
const userId = propUserId || contextValue?.userId;
|
|
1651
|
+
const baseUrl = propBaseUrl || contextValue?.baseUrl || "https://paceful-app.vercel.app/api/v1/partner";
|
|
1652
|
+
const [state, setState] = useState3("LOADING");
|
|
1653
|
+
const [ersData, setErsData] = useState3(null);
|
|
1654
|
+
const isDark = theme === "dark";
|
|
1655
|
+
const ringSize = compact ? 80 : 120;
|
|
1656
|
+
const strokeWidth = compact ? 6 : 8;
|
|
1657
|
+
const radius = (ringSize - strokeWidth) / 2;
|
|
1658
|
+
const circumference = 2 * Math.PI * radius;
|
|
1659
|
+
const fetchERS = async () => {
|
|
1660
|
+
if (!apiKey || !userId) {
|
|
1661
|
+
setState("ERROR");
|
|
1662
|
+
onError?.(new Error("Missing apiKey or userId"));
|
|
1663
|
+
return;
|
|
1664
|
+
}
|
|
1665
|
+
setState("LOADING");
|
|
1666
|
+
try {
|
|
1667
|
+
const response = await fetch(`${baseUrl}/ers/${userId}`, {
|
|
1668
|
+
headers: { "X-API-Key": apiKey }
|
|
1669
|
+
});
|
|
1670
|
+
const data = await response.json();
|
|
1671
|
+
if (!response.ok) {
|
|
1672
|
+
throw new Error(data.error?.message || "Failed to fetch ERS");
|
|
1673
|
+
}
|
|
1674
|
+
const ersResult = {
|
|
1675
|
+
ersScore: data.data?.ersScore || 0,
|
|
1676
|
+
stage: data.data?.stage || "healing",
|
|
1677
|
+
dimensions: data.data?.dimensions || {
|
|
1678
|
+
emotionalStability: 0,
|
|
1679
|
+
selfAwareness: 0,
|
|
1680
|
+
socialConnection: 0,
|
|
1681
|
+
purposeClarity: 0,
|
|
1682
|
+
resilienceGrowth: 0
|
|
1683
|
+
},
|
|
1684
|
+
trend: data.data?.trend
|
|
1685
|
+
};
|
|
1686
|
+
setErsData(ersResult);
|
|
1687
|
+
setState("LOADED");
|
|
1688
|
+
onLoad?.(ersResult);
|
|
1689
|
+
} catch (error) {
|
|
1690
|
+
setState("ERROR");
|
|
1691
|
+
onError?.(error instanceof Error ? error : new Error("Unknown error"));
|
|
1692
|
+
}
|
|
1693
|
+
};
|
|
1694
|
+
useEffect3(() => {
|
|
1695
|
+
if (typeof window !== "undefined") {
|
|
1696
|
+
fetchERS();
|
|
1697
|
+
}
|
|
1698
|
+
}, [apiKey, userId, baseUrl]);
|
|
1699
|
+
const styles = {
|
|
1700
|
+
container: {
|
|
1701
|
+
backgroundColor: isDark ? "#1F1D1A" : "#FFFFFF",
|
|
1702
|
+
borderRadius: 12,
|
|
1703
|
+
padding: compact ? 16 : 20,
|
|
1704
|
+
fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
1705
|
+
color: isDark ? "#FFFFFF" : "#1F1D1A"
|
|
1706
|
+
},
|
|
1707
|
+
ringContainer: {
|
|
1708
|
+
display: "flex",
|
|
1709
|
+
justifyContent: "center",
|
|
1710
|
+
marginBottom: compact ? 12 : 20
|
|
1711
|
+
},
|
|
1712
|
+
ringWrapper: {
|
|
1713
|
+
position: "relative",
|
|
1714
|
+
width: ringSize,
|
|
1715
|
+
height: ringSize
|
|
1716
|
+
},
|
|
1717
|
+
scoreText: {
|
|
1718
|
+
position: "absolute",
|
|
1719
|
+
top: "50%",
|
|
1720
|
+
left: "50%",
|
|
1721
|
+
transform: "translate(-50%, -50%)",
|
|
1722
|
+
textAlign: "center"
|
|
1723
|
+
},
|
|
1724
|
+
scoreNumber: {
|
|
1725
|
+
fontSize: compact ? 24 : 36,
|
|
1726
|
+
fontWeight: 700,
|
|
1727
|
+
lineHeight: 1
|
|
1728
|
+
},
|
|
1729
|
+
stageLabel: {
|
|
1730
|
+
fontSize: 12,
|
|
1731
|
+
textTransform: "uppercase",
|
|
1732
|
+
letterSpacing: 0.5,
|
|
1733
|
+
marginTop: 4,
|
|
1734
|
+
color: isDark ? "#9A938A" : "#6B6560"
|
|
1735
|
+
},
|
|
1736
|
+
trendBadge: (direction) => ({
|
|
1737
|
+
display: "flex",
|
|
1738
|
+
alignItems: "center",
|
|
1739
|
+
justifyContent: "center",
|
|
1740
|
+
gap: 4,
|
|
1741
|
+
fontSize: 13,
|
|
1742
|
+
color: direction === "up" ? "#5B8A72" : direction === "down" ? "#B56B6B" : "#9A938A",
|
|
1743
|
+
marginBottom: 16
|
|
1744
|
+
}),
|
|
1745
|
+
dimensionsContainer: {
|
|
1746
|
+
display: "flex",
|
|
1747
|
+
flexDirection: "column",
|
|
1748
|
+
gap: 12
|
|
1749
|
+
},
|
|
1750
|
+
dimensionRow: {
|
|
1751
|
+
display: "flex",
|
|
1752
|
+
alignItems: "center",
|
|
1753
|
+
gap: 12
|
|
1754
|
+
},
|
|
1755
|
+
dimensionLabel: {
|
|
1756
|
+
fontSize: 13,
|
|
1757
|
+
color: isDark ? "#9A938A" : "#6B6560",
|
|
1758
|
+
width: 120,
|
|
1759
|
+
flexShrink: 0
|
|
1760
|
+
},
|
|
1761
|
+
dimensionBarBg: {
|
|
1762
|
+
flex: 1,
|
|
1763
|
+
height: 8,
|
|
1764
|
+
backgroundColor: isDark ? "#3D3D3A" : "#E5E0D9",
|
|
1765
|
+
borderRadius: 4,
|
|
1766
|
+
overflow: "hidden"
|
|
1767
|
+
},
|
|
1768
|
+
dimensionBarFill: (value) => ({
|
|
1769
|
+
height: "100%",
|
|
1770
|
+
width: `${value}%`,
|
|
1771
|
+
backgroundColor: brandColor,
|
|
1772
|
+
borderRadius: 4,
|
|
1773
|
+
transition: "width 0.5s ease"
|
|
1774
|
+
}),
|
|
1775
|
+
dimensionScore: {
|
|
1776
|
+
fontSize: 13,
|
|
1777
|
+
fontWeight: 500,
|
|
1778
|
+
width: 32,
|
|
1779
|
+
textAlign: "right"
|
|
1780
|
+
},
|
|
1781
|
+
focusBadge: {
|
|
1782
|
+
fontSize: 10,
|
|
1783
|
+
backgroundColor: "#F5E6E6",
|
|
1784
|
+
color: "#B56B6B",
|
|
1785
|
+
padding: "2px 6px",
|
|
1786
|
+
borderRadius: 4,
|
|
1787
|
+
marginLeft: 8
|
|
1788
|
+
},
|
|
1789
|
+
footer: {
|
|
1790
|
+
marginTop: 16,
|
|
1791
|
+
textAlign: "center",
|
|
1792
|
+
fontSize: 11,
|
|
1793
|
+
color: "#9A938A"
|
|
1794
|
+
},
|
|
1795
|
+
skeleton: {
|
|
1796
|
+
animation: "pulse 1.5s ease-in-out infinite",
|
|
1797
|
+
backgroundColor: isDark ? "#3D3D3A" : "#E5E0D9",
|
|
1798
|
+
borderRadius: 8
|
|
1799
|
+
},
|
|
1800
|
+
errorContainer: {
|
|
1801
|
+
textAlign: "center",
|
|
1802
|
+
padding: 20
|
|
1803
|
+
},
|
|
1804
|
+
errorText: {
|
|
1805
|
+
fontSize: 14,
|
|
1806
|
+
color: isDark ? "#9A938A" : "#6B6560",
|
|
1807
|
+
marginBottom: 12
|
|
1808
|
+
},
|
|
1809
|
+
retryButton: {
|
|
1810
|
+
backgroundColor: "transparent",
|
|
1811
|
+
color: brandColor,
|
|
1812
|
+
border: `1px solid ${brandColor}`,
|
|
1813
|
+
borderRadius: 8,
|
|
1814
|
+
padding: "8px 16px",
|
|
1815
|
+
fontSize: 14,
|
|
1816
|
+
cursor: "pointer"
|
|
1817
|
+
}
|
|
1818
|
+
};
|
|
1819
|
+
if (state === "LOADING") {
|
|
1820
|
+
return /* @__PURE__ */ jsxs3("div", { style: styles.container, className, children: [
|
|
1821
|
+
/* @__PURE__ */ jsx3("style", { children: `@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }` }),
|
|
1822
|
+
/* @__PURE__ */ jsx3("div", { style: styles.ringContainer, children: /* @__PURE__ */ jsx3(
|
|
1823
|
+
"div",
|
|
1824
|
+
{
|
|
1825
|
+
style: {
|
|
1826
|
+
...styles.skeleton,
|
|
1827
|
+
width: ringSize,
|
|
1828
|
+
height: ringSize,
|
|
1829
|
+
borderRadius: "50%"
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
) }),
|
|
1833
|
+
showTrend && /* @__PURE__ */ jsx3("div", { style: { display: "flex", justifyContent: "center", marginBottom: 16 }, children: /* @__PURE__ */ jsx3("div", { style: { ...styles.skeleton, width: 120, height: 20 } }) }),
|
|
1834
|
+
showDimensions && !compact && /* @__PURE__ */ jsx3("div", { style: styles.dimensionsContainer, children: [...Array(5)].map((_, i) => /* @__PURE__ */ jsxs3("div", { style: styles.dimensionRow, children: [
|
|
1835
|
+
/* @__PURE__ */ jsx3("div", { style: { ...styles.skeleton, width: 100, height: 16 } }),
|
|
1836
|
+
/* @__PURE__ */ jsx3("div", { style: { ...styles.skeleton, flex: 1, height: 8 } })
|
|
1837
|
+
] }, i)) }),
|
|
1838
|
+
/* @__PURE__ */ jsx3("div", { style: styles.footer, children: "Powered by Paceful" })
|
|
1839
|
+
] });
|
|
1840
|
+
}
|
|
1841
|
+
if (state === "ERROR") {
|
|
1842
|
+
return /* @__PURE__ */ jsxs3("div", { style: styles.container, className, children: [
|
|
1843
|
+
/* @__PURE__ */ jsxs3("div", { style: styles.errorContainer, children: [
|
|
1844
|
+
/* @__PURE__ */ jsx3("p", { style: styles.errorText, children: "Unable to load score" }),
|
|
1845
|
+
/* @__PURE__ */ jsx3("button", { onClick: fetchERS, style: styles.retryButton, children: "Retry" })
|
|
1846
|
+
] }),
|
|
1847
|
+
/* @__PURE__ */ jsx3("div", { style: styles.footer, children: "Powered by Paceful" })
|
|
1848
|
+
] });
|
|
1849
|
+
}
|
|
1850
|
+
if (!ersData) return null;
|
|
1851
|
+
const progress = ersData.ersScore / 100 * circumference;
|
|
1852
|
+
const stageColor = STAGE_COLORS[ersData.stage];
|
|
1853
|
+
return /* @__PURE__ */ jsxs3("div", { style: styles.container, className, children: [
|
|
1854
|
+
/* @__PURE__ */ jsx3("div", { style: styles.ringContainer, children: /* @__PURE__ */ jsxs3("div", { style: styles.ringWrapper, children: [
|
|
1855
|
+
/* @__PURE__ */ jsxs3("svg", { width: ringSize, height: ringSize, children: [
|
|
1856
|
+
/* @__PURE__ */ jsx3(
|
|
1857
|
+
"circle",
|
|
1858
|
+
{
|
|
1859
|
+
cx: ringSize / 2,
|
|
1860
|
+
cy: ringSize / 2,
|
|
1861
|
+
r: radius,
|
|
1862
|
+
stroke: isDark ? "#3D3D3A" : "#E5E0D9",
|
|
1863
|
+
strokeWidth,
|
|
1864
|
+
fill: "none"
|
|
1865
|
+
}
|
|
1866
|
+
),
|
|
1867
|
+
/* @__PURE__ */ jsx3(
|
|
1868
|
+
"circle",
|
|
1869
|
+
{
|
|
1870
|
+
cx: ringSize / 2,
|
|
1871
|
+
cy: ringSize / 2,
|
|
1872
|
+
r: radius,
|
|
1873
|
+
stroke: stageColor,
|
|
1874
|
+
strokeWidth,
|
|
1875
|
+
fill: "none",
|
|
1876
|
+
strokeLinecap: "round",
|
|
1877
|
+
strokeDasharray: circumference,
|
|
1878
|
+
strokeDashoffset: circumference - progress,
|
|
1879
|
+
transform: `rotate(-90 ${ringSize / 2} ${ringSize / 2})`,
|
|
1880
|
+
style: { transition: "stroke-dashoffset 0.5s ease" }
|
|
1881
|
+
}
|
|
1882
|
+
)
|
|
1883
|
+
] }),
|
|
1884
|
+
/* @__PURE__ */ jsxs3("div", { style: styles.scoreText, children: [
|
|
1885
|
+
/* @__PURE__ */ jsx3("div", { style: styles.scoreNumber, children: Math.round(ersData.ersScore) }),
|
|
1886
|
+
/* @__PURE__ */ jsx3("div", { style: styles.stageLabel, children: ersData.stage })
|
|
1887
|
+
] })
|
|
1888
|
+
] }) }),
|
|
1889
|
+
showTrend && ersData.trend && /* @__PURE__ */ jsxs3("div", { style: styles.trendBadge(ersData.trend.direction), children: [
|
|
1890
|
+
ersData.trend.direction === "up" && /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1891
|
+
/* @__PURE__ */ jsx3("svg", { width: "12", height: "12", viewBox: "0 0 12 12", children: /* @__PURE__ */ jsx3("path", { d: "M6 2 L10 6 L7 6 L7 10 L5 10 L5 6 L2 6 Z", fill: "currentColor" }) }),
|
|
1892
|
+
"+",
|
|
1893
|
+
ersData.trend.weeklyChange.toFixed(1),
|
|
1894
|
+
" from last week"
|
|
1895
|
+
] }),
|
|
1896
|
+
ersData.trend.direction === "down" && /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1897
|
+
/* @__PURE__ */ jsx3("svg", { width: "12", height: "12", viewBox: "0 0 12 12", children: /* @__PURE__ */ jsx3("path", { d: "M6 10 L10 6 L7 6 L7 2 L5 2 L5 6 L2 6 Z", fill: "currentColor" }) }),
|
|
1898
|
+
ersData.trend.weeklyChange.toFixed(1),
|
|
1899
|
+
" from last week"
|
|
1900
|
+
] }),
|
|
1901
|
+
ersData.trend.direction === "stable" && /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1902
|
+
/* @__PURE__ */ jsx3("span", { style: { fontSize: 16 }, children: "\u2014" }),
|
|
1903
|
+
"No change"
|
|
1904
|
+
] })
|
|
1905
|
+
] }),
|
|
1906
|
+
showDimensions && !compact && /* @__PURE__ */ jsx3("div", { style: styles.dimensionsContainer, children: DIMENSION_LABELS.map(({ key, label }) => {
|
|
1907
|
+
const value = ersData.dimensions[key] || 0;
|
|
1908
|
+
return /* @__PURE__ */ jsxs3("div", { style: styles.dimensionRow, children: [
|
|
1909
|
+
/* @__PURE__ */ jsx3("span", { style: styles.dimensionLabel, children: label }),
|
|
1910
|
+
/* @__PURE__ */ jsx3("div", { style: styles.dimensionBarBg, children: /* @__PURE__ */ jsx3("div", { style: styles.dimensionBarFill(value) }) }),
|
|
1911
|
+
/* @__PURE__ */ jsx3("span", { style: styles.dimensionScore, children: Math.round(value) }),
|
|
1912
|
+
value < 50 && /* @__PURE__ */ jsx3("span", { style: styles.focusBadge, children: "Focus" })
|
|
1913
|
+
] }, key);
|
|
1914
|
+
}) }),
|
|
1915
|
+
/* @__PURE__ */ jsx3("div", { style: styles.footer, children: "Powered by Paceful" })
|
|
1916
|
+
] });
|
|
1917
|
+
}
|
|
1918
|
+
export {
|
|
1919
|
+
Analytics,
|
|
1920
|
+
ERS,
|
|
1921
|
+
ERSDisplay,
|
|
1922
|
+
Journal,
|
|
1923
|
+
JournalWidget,
|
|
1924
|
+
Mood,
|
|
1925
|
+
MoodWidget,
|
|
1926
|
+
PacefulAuthError,
|
|
1927
|
+
PacefulClient,
|
|
1928
|
+
PacefulError,
|
|
1929
|
+
PacefulNetworkError,
|
|
1930
|
+
PacefulNotFoundError,
|
|
1931
|
+
PacefulProvider,
|
|
1932
|
+
PacefulRateLimitError,
|
|
1933
|
+
PacefulValidationError,
|
|
1934
|
+
Users,
|
|
1935
|
+
Webhooks,
|
|
1936
|
+
PacefulClient as default,
|
|
1937
|
+
usePaceful
|
|
1938
|
+
};
|