convex-feedback 0.1.4 → 0.2.0-beta.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 +87 -27
- package/dist/.tsbuildinfo +1 -1
- package/dist/client/api.d.ts +80 -1
- package/dist/client/api.d.ts.map +1 -1
- package/dist/client/config.d.ts +1 -1
- package/dist/client/index.d.ts +9 -5
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +261 -8
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/api.d.ts +8 -0
- package/dist/component/_generated/api.d.ts.map +1 -1
- package/dist/component/_generated/api.js.map +1 -1
- package/dist/component/_generated/component.d.ts +295 -5
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/admin.d.ts +130 -0
- package/dist/component/admin.d.ts.map +1 -0
- package/dist/component/admin.js +125 -0
- package/dist/component/admin.js.map +1 -0
- package/dist/component/comments.d.ts +7 -5
- package/dist/component/comments.d.ts.map +1 -1
- package/dist/component/comments.js +4 -3
- package/dist/component/comments.js.map +1 -1
- package/dist/component/crons.d.ts +3 -0
- package/dist/component/crons.d.ts.map +1 -0
- package/dist/component/crons.js +8 -0
- package/dist/component/crons.js.map +1 -0
- package/dist/component/entries.d.ts +29 -16
- package/dist/component/entries.d.ts.map +1 -1
- package/dist/component/entries.js +136 -12
- package/dist/component/entries.js.map +1 -1
- package/dist/component/helpers.d.ts +3 -1
- package/dist/component/helpers.d.ts.map +1 -1
- package/dist/component/helpers.js +28 -0
- package/dist/component/helpers.js.map +1 -1
- package/dist/component/migrations.d.ts +11 -0
- package/dist/component/migrations.d.ts.map +1 -0
- package/dist/component/migrations.js +35 -0
- package/dist/component/migrations.js.map +1 -0
- package/dist/component/model.d.ts +144 -24
- package/dist/component/model.d.ts.map +1 -1
- package/dist/component/model.js +34 -1
- package/dist/component/model.js.map +1 -1
- package/dist/component/roadmap.d.ts +166 -0
- package/dist/component/roadmap.d.ts.map +1 -0
- package/dist/component/roadmap.js +534 -0
- package/dist/component/roadmap.js.map +1 -0
- package/dist/component/schema.d.ts +67 -5
- package/dist/component/schema.d.ts.map +1 -1
- package/dist/component/schema.js +57 -2
- package/dist/component/schema.js.map +1 -1
- package/dist/component/tags.d.ts +58 -0
- package/dist/component/tags.d.ts.map +1 -0
- package/dist/component/tags.js +216 -0
- package/dist/component/tags.js.map +1 -0
- package/dist/react/index.d.ts +173 -12
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +78 -1
- package/dist/react/index.js.map +1 -1
- package/dist/test.d.ts +67 -5
- package/dist/test.d.ts.map +1 -1
- package/package.json +6 -2
- package/src/client/README.md +1 -1
- package/src/client/api.ts +183 -0
- package/src/client/config.ts +1 -1
- package/src/client/index.ts +340 -13
- package/src/component/README.md +3 -1
- package/src/component/_generated/api.ts +8 -0
- package/src/component/_generated/component.ts +373 -9
- package/src/component/admin.ts +180 -0
- package/src/component/comments.ts +5 -2
- package/src/component/crons.ts +16 -0
- package/src/component/entries.ts +175 -11
- package/src/component/helpers.ts +44 -1
- package/src/component/migrations.ts +44 -0
- package/src/component/model.ts +79 -4
- package/src/component/roadmap.ts +627 -0
- package/src/component/schema.ts +61 -1
- package/src/react/README.md +2 -2
- package/src/react/index.ts +152 -4
package/src/component/schema.ts
CHANGED
|
@@ -3,8 +3,11 @@ import { v } from "convex/values";
|
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
entryKindValidator,
|
|
6
|
+
entryPriorityValidator,
|
|
7
|
+
entryStatusFilterValidator,
|
|
6
8
|
entryStatusValidator,
|
|
7
9
|
feedbackMetadataValidator,
|
|
10
|
+
roadmapStatusValidator,
|
|
8
11
|
} from "./model.js";
|
|
9
12
|
|
|
10
13
|
const schema = defineSchema({
|
|
@@ -12,6 +15,7 @@ const schema = defineSchema({
|
|
|
12
15
|
actorId: v.string(),
|
|
13
16
|
kind: entryKindValidator,
|
|
14
17
|
status: entryStatusValidator,
|
|
18
|
+
statusFilter: v.optional(entryStatusFilterValidator),
|
|
15
19
|
title: v.string(),
|
|
16
20
|
body: v.string(),
|
|
17
21
|
normalizedTitle: v.string(),
|
|
@@ -20,21 +24,77 @@ const schema = defineSchema({
|
|
|
20
24
|
commentCount: v.number(),
|
|
21
25
|
updatedAt: v.optional(v.number()),
|
|
22
26
|
metadata: v.optional(feedbackMetadataValidator),
|
|
27
|
+
priority: v.optional(entryPriorityValidator),
|
|
28
|
+
roadmapId: v.optional(v.id("roadmap")),
|
|
23
29
|
})
|
|
24
30
|
.index("by_kind", ["kind"])
|
|
25
31
|
.index("by_status", ["status"])
|
|
26
32
|
.index("by_kind_status", ["kind", "status"])
|
|
33
|
+
.index("by_status_filter", ["statusFilter"])
|
|
34
|
+
.index("by_kind_status_filter", ["kind", "statusFilter"])
|
|
27
35
|
.index("by_upvotes", ["upvoteCount"])
|
|
28
36
|
.index("by_kind_upvotes", ["kind", "upvoteCount"])
|
|
29
37
|
.index("by_status_upvotes", ["status", "upvoteCount"])
|
|
30
38
|
.index("by_kind_status_upvotes", ["kind", "status", "upvoteCount"])
|
|
39
|
+
.index("by_status_filter_upvotes", ["statusFilter", "upvoteCount"])
|
|
40
|
+
.index("by_kind_status_filter_upvotes", [
|
|
41
|
+
"kind",
|
|
42
|
+
"statusFilter",
|
|
43
|
+
"upvoteCount",
|
|
44
|
+
])
|
|
31
45
|
.index("by_normalized_title", ["normalizedTitle"])
|
|
32
46
|
.index("by_kind_normalized_title", ["kind", "normalizedTitle"])
|
|
47
|
+
.index("by_priority", ["priority"])
|
|
48
|
+
.index("by_roadmap_id", ["roadmapId"])
|
|
33
49
|
.searchIndex("search", {
|
|
34
50
|
searchField: "searchText",
|
|
35
|
-
filterFields: ["kind", "status"],
|
|
51
|
+
filterFields: ["kind", "status", "statusFilter", "priority"],
|
|
36
52
|
}),
|
|
37
53
|
|
|
54
|
+
roadmap: defineTable({
|
|
55
|
+
title: v.string(),
|
|
56
|
+
description: v.optional(v.string()),
|
|
57
|
+
status: roadmapStatusValidator,
|
|
58
|
+
position: v.number(),
|
|
59
|
+
feedbackCount: v.number(),
|
|
60
|
+
createdAt: v.number(),
|
|
61
|
+
updatedAt: v.number(),
|
|
62
|
+
deletingAt: v.optional(v.number()),
|
|
63
|
+
rebalanceId: v.optional(v.string()),
|
|
64
|
+
rebalanceRank: v.optional(v.number()),
|
|
65
|
+
rebalancePosition: v.optional(v.number()),
|
|
66
|
+
})
|
|
67
|
+
.index("by_status_and_position", ["status", "position"])
|
|
68
|
+
.index("by_status_deleting_at_position", [
|
|
69
|
+
"status",
|
|
70
|
+
"deletingAt",
|
|
71
|
+
"position",
|
|
72
|
+
])
|
|
73
|
+
.index("by_deleting_at_and_position", ["deletingAt", "position"])
|
|
74
|
+
.index("by_status_rebalance_rank", [
|
|
75
|
+
"status",
|
|
76
|
+
"rebalanceId",
|
|
77
|
+
"rebalanceRank",
|
|
78
|
+
])
|
|
79
|
+
.index("by_status_deleting_at_rebalance_id_position", [
|
|
80
|
+
"status",
|
|
81
|
+
"deletingAt",
|
|
82
|
+
"rebalanceId",
|
|
83
|
+
"rebalancePosition",
|
|
84
|
+
])
|
|
85
|
+
.index("by_position", ["position"])
|
|
86
|
+
.searchIndex("search_title", {
|
|
87
|
+
searchField: "title",
|
|
88
|
+
filterFields: ["status", "deletingAt"],
|
|
89
|
+
}),
|
|
90
|
+
|
|
91
|
+
roadmapRebalances: defineTable({
|
|
92
|
+
status: roadmapStatusValidator,
|
|
93
|
+
nextGeneration: v.number(),
|
|
94
|
+
visibleGeneration: v.optional(v.string()),
|
|
95
|
+
activeGeneration: v.optional(v.string()),
|
|
96
|
+
}).index("by_status", ["status"]),
|
|
97
|
+
|
|
38
98
|
comments: defineTable({
|
|
39
99
|
entryId: v.id("entries"),
|
|
40
100
|
parentCommentId: v.optional(v.id("comments")),
|
package/src/react/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# React hooks layer
|
|
2
2
|
|
|
3
|
-
`createFeedbackHooks(api
|
|
3
|
+
`createFeedbackHooks(api?, options?)` binds the host application's public Convex API to headless React hooks. With no API argument it uses `anyApi.feedback`; pass a generated API namespace when the host module has another name.
|
|
4
4
|
|
|
5
|
-
The hooks expose entries, search/similarity, lazy direct-child comment pagination, mutations,
|
|
5
|
+
The hooks expose public entries, search/similarity, lazy direct-child comment pagination, mutations, admin inbox queries, and roadmap operations. Reply queries are not mounted by the hook layer automatically; the UI decides when a comment is expanded.
|
|
6
6
|
|
|
7
7
|
Page-size defaults are client concerns. The server-side component still enforces its configured hard maximum. Keep the two aligned when overriding defaults.
|
package/src/react/index.ts
CHANGED
|
@@ -2,13 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
import { usePaginatedQuery } from "convex-helpers/react";
|
|
4
4
|
import { useMutation, useQuery } from "convex/react";
|
|
5
|
+
import { anyApi } from "convex/server";
|
|
5
6
|
|
|
6
7
|
import type { FeedbackPublicApi } from "../client/api.js";
|
|
7
8
|
import type {
|
|
8
9
|
CommentSort,
|
|
9
10
|
EntryKind,
|
|
11
|
+
EntryPriority,
|
|
10
12
|
EntrySort,
|
|
11
13
|
EntryStatus,
|
|
14
|
+
EntryStatusFilter,
|
|
15
|
+
RoadmapStatus,
|
|
12
16
|
SimilarEntriesResult,
|
|
13
17
|
} from "../component/model.js";
|
|
14
18
|
|
|
@@ -22,7 +26,8 @@ import type {
|
|
|
22
26
|
*/
|
|
23
27
|
export interface FeedbackHooksOptions {
|
|
24
28
|
/**
|
|
25
|
-
* Number of entries initially requested by
|
|
29
|
+
* Number of entries initially requested by entry-list hooks, including the
|
|
30
|
+
* public board, admin inbox, admin search, and roadmap attachments.
|
|
26
31
|
*
|
|
27
32
|
* @default 20
|
|
28
33
|
*/
|
|
@@ -42,6 +47,13 @@ export interface FeedbackHooksOptions {
|
|
|
42
47
|
* @default 10
|
|
43
48
|
*/
|
|
44
49
|
replyPageSize?: number;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Number of roadmap items initially requested by `useRoadmap`.
|
|
53
|
+
*
|
|
54
|
+
* @default 30
|
|
55
|
+
*/
|
|
56
|
+
roadmapPageSize?: number;
|
|
45
57
|
}
|
|
46
58
|
|
|
47
59
|
/**
|
|
@@ -61,6 +73,9 @@ export interface UseEntriesArgs {
|
|
|
61
73
|
/** Optional workflow-status filter. */
|
|
62
74
|
status?: EntryStatus;
|
|
63
75
|
|
|
76
|
+
/** Optional indexed open or closed board bucket. */
|
|
77
|
+
statusFilter?: EntryStatusFilter;
|
|
78
|
+
|
|
64
79
|
/**
|
|
65
80
|
* Server-side entry ordering.
|
|
66
81
|
*
|
|
@@ -120,6 +135,9 @@ export interface SearchEntriesArgs {
|
|
|
120
135
|
/** Optional workflow-status filter. */
|
|
121
136
|
status?: EntryStatus;
|
|
122
137
|
|
|
138
|
+
/** Optional indexed open or closed board bucket. */
|
|
139
|
+
statusFilter?: EntryStatusFilter;
|
|
140
|
+
|
|
123
141
|
/**
|
|
124
142
|
* Maximum number of results requested.
|
|
125
143
|
*
|
|
@@ -129,6 +147,16 @@ export interface SearchEntriesArgs {
|
|
|
129
147
|
limit?: number;
|
|
130
148
|
}
|
|
131
149
|
|
|
150
|
+
export interface UseAdminEntriesArgs {
|
|
151
|
+
kinds?: readonly EntryKind[];
|
|
152
|
+
status?: EntryStatus;
|
|
153
|
+
priority?: EntryPriority;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export interface UseAdminSearchEntriesArgs extends UseAdminEntriesArgs {
|
|
157
|
+
searchQuery: string;
|
|
158
|
+
}
|
|
159
|
+
|
|
132
160
|
/**
|
|
133
161
|
* Arguments accepted by `useSimilarEntries`.
|
|
134
162
|
*/
|
|
@@ -193,6 +221,7 @@ function createFeedbackHooksImplementation<RateLimitResult>(
|
|
|
193
221
|
const entryPageSize = positivePageSize(options.entryPageSize, 20);
|
|
194
222
|
const commentPageSize = positivePageSize(options.commentPageSize, 20);
|
|
195
223
|
const replyPageSize = positivePageSize(options.replyPageSize, 10);
|
|
224
|
+
const roadmapPageSize = positivePageSize(options.roadmapPageSize, 30);
|
|
196
225
|
|
|
197
226
|
return {
|
|
198
227
|
/**
|
|
@@ -203,6 +232,7 @@ function createFeedbackHooksImplementation<RateLimitResult>(
|
|
|
203
232
|
entries: entryPageSize,
|
|
204
233
|
comments: commentPageSize,
|
|
205
234
|
replies: replyPageSize,
|
|
235
|
+
roadmap: roadmapPageSize,
|
|
206
236
|
} as const,
|
|
207
237
|
|
|
208
238
|
/**
|
|
@@ -212,6 +242,9 @@ function createFeedbackHooksImplementation<RateLimitResult>(
|
|
|
212
242
|
const queryArgs = {
|
|
213
243
|
...(args.kinds === undefined ? {} : { kinds: [...args.kinds] }),
|
|
214
244
|
...(args.status === undefined ? {} : { status: args.status }),
|
|
245
|
+
...(args.statusFilter === undefined
|
|
246
|
+
? {}
|
|
247
|
+
: { statusFilter: args.statusFilter }),
|
|
215
248
|
...(args.sort === undefined ? {} : { sort: args.sort }),
|
|
216
249
|
};
|
|
217
250
|
|
|
@@ -244,6 +277,9 @@ function createFeedbackHooksImplementation<RateLimitResult>(
|
|
|
244
277
|
searchQuery,
|
|
245
278
|
...(args.kinds === undefined ? {} : { kinds: [...args.kinds] }),
|
|
246
279
|
...(args.status === undefined ? {} : { status: args.status }),
|
|
280
|
+
...(args.statusFilter === undefined
|
|
281
|
+
? {}
|
|
282
|
+
: { statusFilter: args.statusFilter }),
|
|
247
283
|
...(args.limit === undefined ? {} : { limit: args.limit }),
|
|
248
284
|
};
|
|
249
285
|
|
|
@@ -255,6 +291,80 @@ function createFeedbackHooksImplementation<RateLimitResult>(
|
|
|
255
291
|
return searchQuery.length === 0 ? [] : result;
|
|
256
292
|
},
|
|
257
293
|
|
|
294
|
+
/** Returns whether the authenticated host actor is an admin. */
|
|
295
|
+
useIsAdmin() {
|
|
296
|
+
return useQuery(api.isAdmin, {});
|
|
297
|
+
},
|
|
298
|
+
|
|
299
|
+
/** Returns whether the current request has an authenticated actor. */
|
|
300
|
+
useIsAuthenticated() {
|
|
301
|
+
return useQuery(api.isAuthenticated, {});
|
|
302
|
+
},
|
|
303
|
+
|
|
304
|
+
/** Returns a cursor-paginated admin inbox with private triage relationships. */
|
|
305
|
+
useAdminEntries(args: UseAdminEntriesArgs = {}) {
|
|
306
|
+
return usePaginatedQuery(
|
|
307
|
+
api.adminListEntries,
|
|
308
|
+
{
|
|
309
|
+
...(args.kinds === undefined ? {} : { kinds: [...args.kinds] }),
|
|
310
|
+
...(args.status === undefined ? {} : { status: args.status }),
|
|
311
|
+
...(args.priority === undefined ? {} : { priority: args.priority }),
|
|
312
|
+
},
|
|
313
|
+
{ initialNumItems: entryPageSize },
|
|
314
|
+
);
|
|
315
|
+
},
|
|
316
|
+
|
|
317
|
+
/** Returns one admin-enriched feedback entry. */
|
|
318
|
+
useAdminEntry(entryId: string | null | undefined) {
|
|
319
|
+
return useQuery(
|
|
320
|
+
api.adminGetEntry,
|
|
321
|
+
entryId === null || entryId === undefined ? "skip" : { entryId },
|
|
322
|
+
);
|
|
323
|
+
},
|
|
324
|
+
|
|
325
|
+
/** Searches the admin inbox with triage filters. */
|
|
326
|
+
useAdminSearchEntries(args: UseAdminSearchEntriesArgs) {
|
|
327
|
+
const searchQuery = args.searchQuery.trim();
|
|
328
|
+
return usePaginatedQuery(
|
|
329
|
+
api.adminSearchEntries,
|
|
330
|
+
searchQuery.length === 0
|
|
331
|
+
? "skip"
|
|
332
|
+
: {
|
|
333
|
+
searchQuery,
|
|
334
|
+
...(args.kinds === undefined ? {} : { kinds: [...args.kinds] }),
|
|
335
|
+
...(args.status === undefined ? {} : { status: args.status }),
|
|
336
|
+
...(args.priority === undefined
|
|
337
|
+
? {}
|
|
338
|
+
: { priority: args.priority }),
|
|
339
|
+
},
|
|
340
|
+
{ initialNumItems: entryPageSize },
|
|
341
|
+
);
|
|
342
|
+
},
|
|
343
|
+
|
|
344
|
+
useRoadmap(status?: RoadmapStatus) {
|
|
345
|
+
return usePaginatedQuery(
|
|
346
|
+
api.listRoadmap,
|
|
347
|
+
status === undefined ? {} : { status },
|
|
348
|
+
{ initialNumItems: roadmapPageSize },
|
|
349
|
+
);
|
|
350
|
+
},
|
|
351
|
+
|
|
352
|
+
useSearchRoadmap(searchQuery: string, limit = 10) {
|
|
353
|
+
const normalized = searchQuery.trim();
|
|
354
|
+
return useQuery(
|
|
355
|
+
api.searchRoadmap,
|
|
356
|
+
normalized.length === 0 ? "skip" : { searchQuery: normalized, limit },
|
|
357
|
+
);
|
|
358
|
+
},
|
|
359
|
+
|
|
360
|
+
useRoadmapFeedback(roadmapId: string | null | undefined) {
|
|
361
|
+
return usePaginatedQuery(
|
|
362
|
+
api.listRoadmapFeedback,
|
|
363
|
+
roadmapId === null || roadmapId === undefined ? "skip" : { roadmapId },
|
|
364
|
+
{ initialNumItems: entryPageSize },
|
|
365
|
+
);
|
|
366
|
+
},
|
|
367
|
+
|
|
258
368
|
/**
|
|
259
369
|
* Reactively finds exact and similar entries for a proposed draft.
|
|
260
370
|
*
|
|
@@ -307,6 +417,38 @@ function createFeedbackHooksImplementation<RateLimitResult>(
|
|
|
307
417
|
return useMutation(api.setEntryStatus);
|
|
308
418
|
},
|
|
309
419
|
|
|
420
|
+
useSetEntryPriority() {
|
|
421
|
+
return useMutation(api.setEntryPriority);
|
|
422
|
+
},
|
|
423
|
+
|
|
424
|
+
useCreateRoadmap() {
|
|
425
|
+
return useMutation(api.createRoadmap);
|
|
426
|
+
},
|
|
427
|
+
|
|
428
|
+
useCreateRoadmapForEntry() {
|
|
429
|
+
return useMutation(api.createRoadmapForEntry);
|
|
430
|
+
},
|
|
431
|
+
|
|
432
|
+
useUpdateRoadmap() {
|
|
433
|
+
return useMutation(api.updateRoadmap);
|
|
434
|
+
},
|
|
435
|
+
|
|
436
|
+
useDeleteRoadmap() {
|
|
437
|
+
return useMutation(api.deleteRoadmap);
|
|
438
|
+
},
|
|
439
|
+
|
|
440
|
+
useMoveRoadmapItem() {
|
|
441
|
+
return useMutation(api.moveRoadmapItem);
|
|
442
|
+
},
|
|
443
|
+
|
|
444
|
+
useAttachFeedbackToRoadmap() {
|
|
445
|
+
return useMutation(api.attachFeedbackToRoadmap);
|
|
446
|
+
},
|
|
447
|
+
|
|
448
|
+
useDetachFeedbackFromRoadmap() {
|
|
449
|
+
return useMutation(api.detachFeedbackFromRoadmap);
|
|
450
|
+
},
|
|
451
|
+
|
|
310
452
|
/** Returns the idempotent entry-upvote state mutation. */
|
|
311
453
|
useSetEntryUpvote() {
|
|
312
454
|
return useMutation(api.setEntryUpvote);
|
|
@@ -340,18 +482,24 @@ type CreatedFeedbackHooks<RateLimitResult> = ReturnType<
|
|
|
340
482
|
|
|
341
483
|
/** Creates hooks for a feedback API whose rate limiters reject by throwing. */
|
|
342
484
|
export function createFeedbackHooks(
|
|
343
|
-
api
|
|
485
|
+
api?: FeedbackPublicApi<string | undefined, never>,
|
|
344
486
|
options?: FeedbackHooksOptions,
|
|
345
487
|
): CreatedFeedbackHooks<never>;
|
|
346
488
|
|
|
347
489
|
/** Creates hooks carrying a validated non-throwing rate-limit result. */
|
|
348
490
|
export function createFeedbackHooks<RateLimitResult>(
|
|
349
|
-
api
|
|
491
|
+
api?: FeedbackPublicApi<string | undefined, RateLimitResult>,
|
|
350
492
|
options?: FeedbackHooksOptions,
|
|
351
493
|
): CreatedFeedbackHooks<RateLimitResult>;
|
|
352
494
|
|
|
353
495
|
export function createFeedbackHooks<RateLimitResult>(
|
|
354
|
-
api: FeedbackPublicApi<
|
|
496
|
+
api: FeedbackPublicApi<
|
|
497
|
+
string | undefined,
|
|
498
|
+
RateLimitResult
|
|
499
|
+
> = anyApi.feedback as unknown as FeedbackPublicApi<
|
|
500
|
+
string | undefined,
|
|
501
|
+
RateLimitResult
|
|
502
|
+
>,
|
|
355
503
|
options: FeedbackHooksOptions = {},
|
|
356
504
|
): CreatedFeedbackHooks<RateLimitResult> {
|
|
357
505
|
return createFeedbackHooksImplementation(api, options);
|