@appgram/react 0.1.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/LICENSE +21 -0
- package/dist/chunk-3UBJGXCO.js +6368 -0
- package/dist/chunk-3UBJGXCO.js.map +1 -0
- package/dist/chunk-75P634IK.js +758 -0
- package/dist/chunk-75P634IK.js.map +1 -0
- package/dist/chunk-AIDLOCVJ.mjs +6341 -0
- package/dist/chunk-AIDLOCVJ.mjs.map +1 -0
- package/dist/chunk-KPIKYXAN.mjs +47 -0
- package/dist/chunk-KPIKYXAN.mjs.map +1 -0
- package/dist/chunk-N6PJDQCU.mjs +741 -0
- package/dist/chunk-N6PJDQCU.mjs.map +1 -0
- package/dist/chunk-ZJZ3A2S3.js +49 -0
- package/dist/chunk-ZJZ3A2S3.js.map +1 -0
- package/dist/components/index.d.mts +1283 -0
- package/dist/components/index.d.ts +1283 -0
- package/dist/components/index.js +85 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/index.mjs +4 -0
- package/dist/components/index.mjs.map +1 -0
- package/dist/hooks/index.d.mts +572 -0
- package/dist/hooks/index.d.ts +572 -0
- package/dist/hooks/index.js +53 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/index.mjs +4 -0
- package/dist/hooks/index.mjs.map +1 -0
- package/dist/index.d.mts +399 -0
- package/dist/index.d.ts +399 -0
- package/dist/index.js +783 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +645 -0
- package/dist/index.mjs.map +1 -0
- package/dist/useVote-CLhkwtLT.d.mts +420 -0
- package/dist/useVote-CLhkwtLT.d.ts +420 -0
- package/package.json +82 -0
- package/tailwind-preset.js +48 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,645 @@
|
|
|
1
|
+
export { useWish } from './chunk-KPIKYXAN.mjs';
|
|
2
|
+
export { HelpArticleDetail, HelpArticles, HelpCenter, HelpCollections, ReleaseCard, ReleaseDetail, ReleaseList, Releases, RoadmapBoard, RoadmapColumn, StatusBoard, StatusIncidentDetail, SubmitWishForm, SupportForm, VoteButton, WhatsNewPopup, WishCard, WishDetail, WishList } from './chunk-AIDLOCVJ.mjs';
|
|
3
|
+
import { getFingerprint, AppgramContext } from './chunk-N6PJDQCU.mjs';
|
|
4
|
+
export { cn, getFingerprint, resetFingerprint, useAppgramContext, useComments, useHelpArticle, useHelpCenter, useHelpFlow, useRelease, useReleases, useRoadmap, useSupport, useVote, useWishes } from './chunk-N6PJDQCU.mjs';
|
|
5
|
+
import { useState, useEffect, useMemo } from 'react';
|
|
6
|
+
import { jsx } from 'react/jsx-runtime';
|
|
7
|
+
|
|
8
|
+
// src/client/AppgramClient.ts
|
|
9
|
+
var AppgramClient = class {
|
|
10
|
+
constructor(config) {
|
|
11
|
+
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
12
|
+
this.projectId = config.projectId;
|
|
13
|
+
this.orgSlug = config.orgSlug;
|
|
14
|
+
this.projectSlug = config.projectSlug;
|
|
15
|
+
}
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// HTTP Methods
|
|
18
|
+
// ============================================================================
|
|
19
|
+
async request(method, endpoint, options) {
|
|
20
|
+
let url = `${this.baseUrl}${endpoint}`;
|
|
21
|
+
if (options?.params) {
|
|
22
|
+
const searchParams = new URLSearchParams();
|
|
23
|
+
Object.entries(options.params).forEach(([key, value]) => {
|
|
24
|
+
if (value !== void 0 && value !== null && value !== "") {
|
|
25
|
+
searchParams.append(key, value);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
const queryString = searchParams.toString();
|
|
29
|
+
if (queryString) {
|
|
30
|
+
url += `?${queryString}`;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
const response = await fetch(url, {
|
|
35
|
+
method,
|
|
36
|
+
headers: {
|
|
37
|
+
"Content-Type": "application/json"
|
|
38
|
+
},
|
|
39
|
+
body: options?.body ? JSON.stringify(options.body) : void 0
|
|
40
|
+
});
|
|
41
|
+
const data = await response.json();
|
|
42
|
+
if (!response.ok) {
|
|
43
|
+
return {
|
|
44
|
+
success: false,
|
|
45
|
+
error: {
|
|
46
|
+
code: String(response.status),
|
|
47
|
+
message: data.message || data.error || "An error occurred"
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
if (data && typeof data === "object" && "success" in data) {
|
|
52
|
+
return data;
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
success: true,
|
|
56
|
+
data
|
|
57
|
+
};
|
|
58
|
+
} catch (error) {
|
|
59
|
+
return {
|
|
60
|
+
success: false,
|
|
61
|
+
error: {
|
|
62
|
+
code: "NETWORK_ERROR",
|
|
63
|
+
message: error instanceof Error ? error.message : "Network error"
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
get(endpoint, params) {
|
|
69
|
+
return this.request("GET", endpoint, { params });
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Raw request that returns the API response as-is without transformation
|
|
73
|
+
*/
|
|
74
|
+
async requestRaw(endpoint, params) {
|
|
75
|
+
let url = `${this.baseUrl}${endpoint}`;
|
|
76
|
+
if (params) {
|
|
77
|
+
const searchParams = new URLSearchParams();
|
|
78
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
79
|
+
if (value !== void 0 && value !== null && value !== "") {
|
|
80
|
+
searchParams.append(key, value);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
const queryString = searchParams.toString();
|
|
84
|
+
if (queryString) {
|
|
85
|
+
url += `?${queryString}`;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
const response = await fetch(url, {
|
|
90
|
+
method: "GET",
|
|
91
|
+
headers: {
|
|
92
|
+
"Content-Type": "application/json"
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
const data = await response.json();
|
|
96
|
+
if (!response.ok) {
|
|
97
|
+
return {
|
|
98
|
+
success: false,
|
|
99
|
+
error: {
|
|
100
|
+
code: String(response.status),
|
|
101
|
+
message: data.message || data.error || "An error occurred"
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
return data;
|
|
106
|
+
} catch (error) {
|
|
107
|
+
return {
|
|
108
|
+
success: false,
|
|
109
|
+
error: {
|
|
110
|
+
code: "NETWORK_ERROR",
|
|
111
|
+
message: error instanceof Error ? error.message : "Network error"
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
post(endpoint, body) {
|
|
117
|
+
return this.request("POST", endpoint, { body });
|
|
118
|
+
}
|
|
119
|
+
delete(endpoint) {
|
|
120
|
+
return this.request("DELETE", endpoint);
|
|
121
|
+
}
|
|
122
|
+
// ============================================================================
|
|
123
|
+
// Wishes
|
|
124
|
+
// ============================================================================
|
|
125
|
+
/**
|
|
126
|
+
* Get public wishes for the project
|
|
127
|
+
*/
|
|
128
|
+
async getPublicWishes(filters) {
|
|
129
|
+
const params = {
|
|
130
|
+
project_id: this.projectId
|
|
131
|
+
};
|
|
132
|
+
if (filters?.status) {
|
|
133
|
+
params.status = Array.isArray(filters.status) ? filters.status.join(",") : filters.status;
|
|
134
|
+
}
|
|
135
|
+
if (filters?.category_id) params.category_id = filters.category_id;
|
|
136
|
+
if (filters?.search) params.search = filters.search;
|
|
137
|
+
if (filters?.sort_by) params.sort_by = filters.sort_by;
|
|
138
|
+
if (filters?.sort_order) params.sort_order = filters.sort_order;
|
|
139
|
+
if (filters?.page) params.page = String(filters.page);
|
|
140
|
+
if (filters?.per_page) params.per_page = String(filters.per_page);
|
|
141
|
+
if (filters?.fingerprint) params.fingerprint = filters.fingerprint;
|
|
142
|
+
const rawResponse = await this.requestRaw("/portal/wishes", params);
|
|
143
|
+
if (!rawResponse.success) {
|
|
144
|
+
return {
|
|
145
|
+
success: false,
|
|
146
|
+
error: rawResponse.error
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
success: true,
|
|
151
|
+
data: {
|
|
152
|
+
data: rawResponse.data || [],
|
|
153
|
+
total: rawResponse.total || 0,
|
|
154
|
+
page: rawResponse.page || 1,
|
|
155
|
+
per_page: rawResponse.per_page || 20,
|
|
156
|
+
total_pages: rawResponse.total_pages || 0
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Get a single wish by ID
|
|
162
|
+
*/
|
|
163
|
+
async getWish(wishId) {
|
|
164
|
+
return this.get(`/portal/wishes/${wishId}`, {
|
|
165
|
+
project_id: this.projectId
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Create a new wish (feature request)
|
|
170
|
+
*/
|
|
171
|
+
async createWish(data) {
|
|
172
|
+
return this.post("/portal/wishes", {
|
|
173
|
+
project_id: this.projectId,
|
|
174
|
+
...data
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
// ============================================================================
|
|
178
|
+
// Votes
|
|
179
|
+
// ============================================================================
|
|
180
|
+
/**
|
|
181
|
+
* Check if a fingerprint has voted on a wish
|
|
182
|
+
*/
|
|
183
|
+
async checkVote(wishId, fingerprint) {
|
|
184
|
+
return this.get(`/api/v1/votes/check/${wishId}`, {
|
|
185
|
+
fingerprint
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Create a vote
|
|
190
|
+
*/
|
|
191
|
+
async createVote(wishId, fingerprint, voterEmail) {
|
|
192
|
+
return this.post("/api/v1/votes", {
|
|
193
|
+
wish_id: wishId,
|
|
194
|
+
fingerprint,
|
|
195
|
+
voter_email: voterEmail
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Delete a vote
|
|
200
|
+
*/
|
|
201
|
+
async deleteVote(voteId) {
|
|
202
|
+
return this.delete(`/api/v1/votes/${voteId}`);
|
|
203
|
+
}
|
|
204
|
+
// ============================================================================
|
|
205
|
+
// Comments
|
|
206
|
+
// ============================================================================
|
|
207
|
+
/**
|
|
208
|
+
* Get comments for a wish
|
|
209
|
+
*/
|
|
210
|
+
async getComments(wishId, options) {
|
|
211
|
+
const params = {
|
|
212
|
+
wish_id: wishId
|
|
213
|
+
};
|
|
214
|
+
if (options?.page) params.page = String(options.page);
|
|
215
|
+
if (options?.per_page) params.per_page = String(options.per_page);
|
|
216
|
+
const response = await this.get("/api/v1/comments", params);
|
|
217
|
+
if (!response.success) {
|
|
218
|
+
return {
|
|
219
|
+
success: false,
|
|
220
|
+
error: response.error
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
const comments = response.data || [];
|
|
224
|
+
return {
|
|
225
|
+
success: true,
|
|
226
|
+
data: {
|
|
227
|
+
data: comments,
|
|
228
|
+
total: comments.length,
|
|
229
|
+
page: options?.page || 1,
|
|
230
|
+
per_page: options?.per_page || 20,
|
|
231
|
+
total_pages: 1
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Create a comment
|
|
237
|
+
*/
|
|
238
|
+
async createComment(data) {
|
|
239
|
+
return this.post("/api/v1/comments", {
|
|
240
|
+
...data,
|
|
241
|
+
author_type: "anonymous",
|
|
242
|
+
is_official: false
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
// ============================================================================
|
|
246
|
+
// Roadmap
|
|
247
|
+
// ============================================================================
|
|
248
|
+
/**
|
|
249
|
+
* Get roadmap data for the project
|
|
250
|
+
*/
|
|
251
|
+
async getRoadmapData() {
|
|
252
|
+
const params = {
|
|
253
|
+
project_id: this.projectId
|
|
254
|
+
};
|
|
255
|
+
if (this.orgSlug && this.projectSlug) {
|
|
256
|
+
params.org_slug = this.orgSlug;
|
|
257
|
+
params.project_slug = this.projectSlug;
|
|
258
|
+
}
|
|
259
|
+
return this.get("/portal/roadmap-data", params);
|
|
260
|
+
}
|
|
261
|
+
// ============================================================================
|
|
262
|
+
// Releases
|
|
263
|
+
// ============================================================================
|
|
264
|
+
/**
|
|
265
|
+
* Get public releases for the project
|
|
266
|
+
*/
|
|
267
|
+
async getReleases(options) {
|
|
268
|
+
if (!this.orgSlug || !this.projectSlug) {
|
|
269
|
+
return {
|
|
270
|
+
success: false,
|
|
271
|
+
error: {
|
|
272
|
+
code: "MISSING_SLUGS",
|
|
273
|
+
message: "orgSlug and projectSlug are required for releases endpoint"
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
const params = {};
|
|
278
|
+
if (options?.limit) params.limit = String(options.limit);
|
|
279
|
+
return this.get(
|
|
280
|
+
`/api/v1/releases/public/${this.orgSlug}/${this.projectSlug}`,
|
|
281
|
+
params
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Get a single release by slug
|
|
286
|
+
*/
|
|
287
|
+
async getRelease(releaseSlug) {
|
|
288
|
+
if (!this.orgSlug || !this.projectSlug) {
|
|
289
|
+
return {
|
|
290
|
+
success: false,
|
|
291
|
+
error: {
|
|
292
|
+
code: "MISSING_SLUGS",
|
|
293
|
+
message: "orgSlug and projectSlug are required for releases endpoint"
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
return this.get(
|
|
298
|
+
`/api/v1/releases/public/${this.orgSlug}/${this.projectSlug}/${releaseSlug}`
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Get features for a release (public endpoint)
|
|
303
|
+
*/
|
|
304
|
+
async getReleaseFeatures(releaseSlug) {
|
|
305
|
+
if (!this.orgSlug || !this.projectSlug) {
|
|
306
|
+
return {
|
|
307
|
+
success: false,
|
|
308
|
+
error: {
|
|
309
|
+
code: "MISSING_SLUGS",
|
|
310
|
+
message: "orgSlug and projectSlug are required for release features endpoint"
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
return this.get(
|
|
315
|
+
`/api/v1/releases/public/${this.orgSlug}/${this.projectSlug}/${releaseSlug}/features`
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
// ============================================================================
|
|
319
|
+
// Help Center
|
|
320
|
+
// ============================================================================
|
|
321
|
+
/**
|
|
322
|
+
* Get help center collection for the project
|
|
323
|
+
*/
|
|
324
|
+
async getHelpCollection() {
|
|
325
|
+
return this.get("/portal/help", {
|
|
326
|
+
project_id: this.projectId
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Get a help flow by slug
|
|
331
|
+
*/
|
|
332
|
+
async getHelpFlow(slug) {
|
|
333
|
+
return this.get(`/portal/help/flows/${slug}`, {
|
|
334
|
+
project_id: this.projectId
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Get a help article by slug
|
|
339
|
+
*/
|
|
340
|
+
async getHelpArticle(slug, flowId) {
|
|
341
|
+
return this.get(`/portal/help/articles/${slug}`, {
|
|
342
|
+
flow_id: flowId
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
// ============================================================================
|
|
346
|
+
// Support
|
|
347
|
+
// ============================================================================
|
|
348
|
+
/**
|
|
349
|
+
* Upload a file via public portal (no auth required)
|
|
350
|
+
*/
|
|
351
|
+
async uploadFile(file) {
|
|
352
|
+
const url = `${this.baseUrl}/portal/files/upload`;
|
|
353
|
+
const formData = new FormData();
|
|
354
|
+
formData.append("file", file);
|
|
355
|
+
formData.append("project_id", this.projectId);
|
|
356
|
+
try {
|
|
357
|
+
const response = await fetch(url, {
|
|
358
|
+
method: "POST",
|
|
359
|
+
body: formData
|
|
360
|
+
});
|
|
361
|
+
const data = await response.json();
|
|
362
|
+
if (!response.ok) {
|
|
363
|
+
return {
|
|
364
|
+
success: false,
|
|
365
|
+
error: {
|
|
366
|
+
code: data.error?.code || "UPLOAD_ERROR",
|
|
367
|
+
message: data.error?.message || data.message || "File upload failed"
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
return {
|
|
372
|
+
success: true,
|
|
373
|
+
data: data.data || data
|
|
374
|
+
};
|
|
375
|
+
} catch (error) {
|
|
376
|
+
return {
|
|
377
|
+
success: false,
|
|
378
|
+
error: {
|
|
379
|
+
code: "UPLOAD_ERROR",
|
|
380
|
+
message: error instanceof Error ? error.message : "File upload failed"
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Submit a support request
|
|
387
|
+
*/
|
|
388
|
+
async submitSupportRequest(data) {
|
|
389
|
+
const uploadedAttachments = [];
|
|
390
|
+
if (data.attachments && data.attachments.length > 0) {
|
|
391
|
+
for (const file of data.attachments) {
|
|
392
|
+
if (file.size > 10 * 1024 * 1024) {
|
|
393
|
+
return {
|
|
394
|
+
success: false,
|
|
395
|
+
error: {
|
|
396
|
+
code: "FILE_TOO_LARGE",
|
|
397
|
+
message: `File "${file.name}" is too large. Maximum size is 10MB.`
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
const uploadResponse = await this.uploadFile(file);
|
|
402
|
+
if (uploadResponse.success && uploadResponse.data) {
|
|
403
|
+
uploadedAttachments.push({
|
|
404
|
+
url: uploadResponse.data.url,
|
|
405
|
+
name: uploadResponse.data.name,
|
|
406
|
+
size: uploadResponse.data.size,
|
|
407
|
+
mime_type: uploadResponse.data.mime_type
|
|
408
|
+
});
|
|
409
|
+
} else {
|
|
410
|
+
return {
|
|
411
|
+
success: false,
|
|
412
|
+
error: uploadResponse.error || {
|
|
413
|
+
code: "UPLOAD_ERROR",
|
|
414
|
+
message: "Failed to upload attachment"
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
const payload = {
|
|
421
|
+
project_id: this.projectId,
|
|
422
|
+
subject: data.subject,
|
|
423
|
+
description: data.description,
|
|
424
|
+
user_email: data.user_email
|
|
425
|
+
};
|
|
426
|
+
if (data.user_name) payload.user_name = data.user_name;
|
|
427
|
+
if (data.category) payload.category = data.category;
|
|
428
|
+
if (uploadedAttachments.length > 0) payload.attachments = uploadedAttachments;
|
|
429
|
+
return this.post("/portal/support-requests", payload);
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Request a magic link to access support tickets
|
|
433
|
+
*/
|
|
434
|
+
async sendSupportMagicLink(userEmail) {
|
|
435
|
+
return this.post("/portal/support-requests/send-magic-link", {
|
|
436
|
+
project_id: this.projectId,
|
|
437
|
+
user_email: userEmail
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Verify magic link token and get user's support tickets
|
|
442
|
+
*/
|
|
443
|
+
async verifySupportToken(token) {
|
|
444
|
+
return this.get(
|
|
445
|
+
"/portal/support-requests/verify-token",
|
|
446
|
+
{
|
|
447
|
+
token,
|
|
448
|
+
project_id: this.projectId
|
|
449
|
+
}
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Get a specific support ticket using magic link token
|
|
454
|
+
*/
|
|
455
|
+
async getSupportTicket(ticketId, token) {
|
|
456
|
+
return this.get(`/portal/support-requests/${ticketId}`, {
|
|
457
|
+
token
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Add a message to a support ticket
|
|
462
|
+
*/
|
|
463
|
+
async addSupportMessage(ticketId, token, content) {
|
|
464
|
+
const url = `/portal/support-requests/${ticketId}/messages`;
|
|
465
|
+
const fullUrl = `${this.baseUrl}${url}`;
|
|
466
|
+
try {
|
|
467
|
+
const response = await fetch(fullUrl, {
|
|
468
|
+
method: "POST",
|
|
469
|
+
headers: {
|
|
470
|
+
"Content-Type": "application/json",
|
|
471
|
+
"Authorization": `Bearer ${token}`
|
|
472
|
+
},
|
|
473
|
+
body: JSON.stringify({ content })
|
|
474
|
+
});
|
|
475
|
+
const data = await response.json();
|
|
476
|
+
if (!response.ok) {
|
|
477
|
+
return {
|
|
478
|
+
success: false,
|
|
479
|
+
error: {
|
|
480
|
+
code: String(response.status),
|
|
481
|
+
message: data.message || data.error || "An error occurred"
|
|
482
|
+
}
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
if (data && typeof data === "object" && "success" in data) {
|
|
486
|
+
return data;
|
|
487
|
+
}
|
|
488
|
+
return {
|
|
489
|
+
success: true,
|
|
490
|
+
data
|
|
491
|
+
};
|
|
492
|
+
} catch (error) {
|
|
493
|
+
return {
|
|
494
|
+
success: false,
|
|
495
|
+
error: {
|
|
496
|
+
code: "NETWORK_ERROR",
|
|
497
|
+
message: error instanceof Error ? error.message : "Network error"
|
|
498
|
+
}
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
// ============================================================================
|
|
503
|
+
// Page Data (Combined)
|
|
504
|
+
// ============================================================================
|
|
505
|
+
/**
|
|
506
|
+
* Get all public page data in one request
|
|
507
|
+
*/
|
|
508
|
+
async getPageData() {
|
|
509
|
+
const params = {
|
|
510
|
+
project_id: this.projectId
|
|
511
|
+
};
|
|
512
|
+
if (this.orgSlug) params.org_slug = this.orgSlug;
|
|
513
|
+
if (this.projectSlug) params.project_slug = this.projectSlug;
|
|
514
|
+
return this.get("/portal/page-data", params);
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
var DEFAULT_API_URL = "https://api.appgram.dev";
|
|
518
|
+
var DEFAULT_LIGHT_COLORS = {
|
|
519
|
+
primary: "#0EA5E9",
|
|
520
|
+
// Arctic blue
|
|
521
|
+
secondary: "#6B7280",
|
|
522
|
+
// Gray
|
|
523
|
+
accent: "#0EA5E9",
|
|
524
|
+
// Arctic blue
|
|
525
|
+
background: "#FFFFFF",
|
|
526
|
+
// White
|
|
527
|
+
text: "#242424",
|
|
528
|
+
// Near-black
|
|
529
|
+
cardBackground: "#F7F7F7",
|
|
530
|
+
cardText: "#242424"
|
|
531
|
+
};
|
|
532
|
+
var DEFAULT_DARK_COLORS = {
|
|
533
|
+
primary: "#38BDF8",
|
|
534
|
+
// Lighter arctic blue
|
|
535
|
+
secondary: "#3A3A3A",
|
|
536
|
+
// Dark gray (subtle for borders)
|
|
537
|
+
accent: "#38BDF8",
|
|
538
|
+
// Lighter arctic blue
|
|
539
|
+
background: "#0A0A0A",
|
|
540
|
+
// Near-black
|
|
541
|
+
text: "#E5E5E5",
|
|
542
|
+
// Light gray
|
|
543
|
+
cardBackground: "#1A1A1A",
|
|
544
|
+
cardText: "#E5E5E5"
|
|
545
|
+
};
|
|
546
|
+
var DEFAULT_THEME = {
|
|
547
|
+
typography: {
|
|
548
|
+
fontFamily: "inherit"
|
|
549
|
+
},
|
|
550
|
+
borderRadius: 8
|
|
551
|
+
};
|
|
552
|
+
function getSystemIsDark() {
|
|
553
|
+
if (typeof window === "undefined") return false;
|
|
554
|
+
return window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
555
|
+
}
|
|
556
|
+
function resolveIsDark(mode) {
|
|
557
|
+
if (mode === "dark") return true;
|
|
558
|
+
if (mode === "light") return false;
|
|
559
|
+
return getSystemIsDark();
|
|
560
|
+
}
|
|
561
|
+
function AppgramProvider({
|
|
562
|
+
config,
|
|
563
|
+
children
|
|
564
|
+
}) {
|
|
565
|
+
const [fingerprint, setFingerprint] = useState(null);
|
|
566
|
+
const themeMode = config.theme?.mode ?? "system";
|
|
567
|
+
const [isDark, setIsDark] = useState(() => resolveIsDark(themeMode));
|
|
568
|
+
useEffect(() => {
|
|
569
|
+
if (config.enableFingerprinting !== false) {
|
|
570
|
+
setFingerprint(getFingerprint());
|
|
571
|
+
}
|
|
572
|
+
}, [config.enableFingerprinting]);
|
|
573
|
+
useEffect(() => {
|
|
574
|
+
if (themeMode !== "system" || typeof window === "undefined") {
|
|
575
|
+
setIsDark(resolveIsDark(themeMode));
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
579
|
+
setIsDark(mediaQuery.matches);
|
|
580
|
+
const handler = (e) => setIsDark(e.matches);
|
|
581
|
+
mediaQuery.addEventListener("change", handler);
|
|
582
|
+
return () => mediaQuery.removeEventListener("change", handler);
|
|
583
|
+
}, [themeMode]);
|
|
584
|
+
const client = useMemo(() => {
|
|
585
|
+
return new AppgramClient({
|
|
586
|
+
baseUrl: config.apiUrl || DEFAULT_API_URL,
|
|
587
|
+
projectId: config.projectId,
|
|
588
|
+
orgSlug: config.orgSlug,
|
|
589
|
+
projectSlug: config.projectSlug
|
|
590
|
+
});
|
|
591
|
+
}, [config.apiUrl, config.projectId, config.orgSlug, config.projectSlug]);
|
|
592
|
+
const lightColors = useMemo(() => ({
|
|
593
|
+
...DEFAULT_LIGHT_COLORS,
|
|
594
|
+
...config.theme?.colors
|
|
595
|
+
}), [config.theme?.colors]);
|
|
596
|
+
const darkColors = useMemo(() => ({
|
|
597
|
+
...DEFAULT_DARK_COLORS,
|
|
598
|
+
...config.theme?.darkColors
|
|
599
|
+
}), [config.theme?.darkColors]);
|
|
600
|
+
const currentColors = isDark ? darkColors : lightColors;
|
|
601
|
+
const theme = useMemo(() => {
|
|
602
|
+
return {
|
|
603
|
+
mode: themeMode,
|
|
604
|
+
colors: lightColors,
|
|
605
|
+
darkColors,
|
|
606
|
+
typography: {
|
|
607
|
+
...DEFAULT_THEME.typography,
|
|
608
|
+
...config.theme?.typography
|
|
609
|
+
},
|
|
610
|
+
borderRadius: config.theme?.borderRadius ?? DEFAULT_THEME.borderRadius,
|
|
611
|
+
// Include resolved state for components
|
|
612
|
+
isDark,
|
|
613
|
+
currentColors
|
|
614
|
+
};
|
|
615
|
+
}, [themeMode, lightColors, darkColors, config.theme?.typography, config.theme?.borderRadius, isDark, currentColors]);
|
|
616
|
+
const contextValue = useMemo(() => ({
|
|
617
|
+
config: {
|
|
618
|
+
...config,
|
|
619
|
+
apiUrl: config.apiUrl || DEFAULT_API_URL
|
|
620
|
+
},
|
|
621
|
+
client,
|
|
622
|
+
fingerprint,
|
|
623
|
+
theme
|
|
624
|
+
}), [config, client, fingerprint, theme]);
|
|
625
|
+
useEffect(() => {
|
|
626
|
+
if (typeof document === "undefined") return;
|
|
627
|
+
const root = document.documentElement;
|
|
628
|
+
const colors = currentColors;
|
|
629
|
+
root.setAttribute("data-appgram-theme", isDark ? "dark" : "light");
|
|
630
|
+
if (colors.primary) root.style.setProperty("--appgram-primary", colors.primary);
|
|
631
|
+
if (colors.secondary) root.style.setProperty("--appgram-secondary", colors.secondary);
|
|
632
|
+
if (colors.accent) root.style.setProperty("--appgram-accent", colors.accent);
|
|
633
|
+
if (colors.background) root.style.setProperty("--appgram-background", colors.background);
|
|
634
|
+
if (colors.text) root.style.setProperty("--appgram-foreground", colors.text);
|
|
635
|
+
if (colors.cardBackground) root.style.setProperty("--appgram-card", colors.cardBackground);
|
|
636
|
+
if (colors.cardText) root.style.setProperty("--appgram-card-foreground", colors.cardText);
|
|
637
|
+
if (theme.borderRadius) root.style.setProperty("--appgram-radius", `${theme.borderRadius}px`);
|
|
638
|
+
if (theme.typography?.fontFamily) root.style.setProperty("--appgram-font-family", theme.typography.fontFamily);
|
|
639
|
+
}, [currentColors, isDark, theme.borderRadius, theme.typography?.fontFamily]);
|
|
640
|
+
return /* @__PURE__ */ jsx(AppgramContext.Provider, { value: contextValue, children });
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
export { AppgramClient, AppgramProvider };
|
|
644
|
+
//# sourceMappingURL=index.mjs.map
|
|
645
|
+
//# sourceMappingURL=index.mjs.map
|