convex-feedback-ui 0.1.5 → 0.1.7
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 +143 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/native/expo/ExpoFeedbackBody.d.ts +1 -1
- package/dist/native/expo/ExpoFeedbackBody.d.ts.map +1 -1
- package/dist/native/expo/ExpoFeedbackBody.js +2 -2
- package/dist/native/expo/ExpoFeedbackBody.js.map +1 -1
- package/dist/native/expo/FeedbackCreateStackLayout.d.ts +3 -0
- package/dist/native/expo/FeedbackCreateStackLayout.d.ts.map +1 -0
- package/dist/native/expo/FeedbackCreateStackLayout.js +35 -0
- package/dist/native/expo/FeedbackCreateStackLayout.js.map +1 -0
- package/dist/native/expo/FeedbackRoutes.d.ts +4 -0
- package/dist/native/expo/FeedbackRoutes.d.ts.map +1 -0
- package/dist/native/expo/FeedbackRoutes.js +81 -0
- package/dist/native/expo/FeedbackRoutes.js.map +1 -0
- package/dist/native/expo/FeedbackScreen.d.ts +1 -1
- package/dist/native/expo/FeedbackScreen.d.ts.map +1 -1
- package/dist/native/expo/FeedbackScreen.js +2 -2
- package/dist/native/expo/FeedbackScreen.js.map +1 -1
- package/dist/native/expo/FeedbackStack.d.ts +1 -1
- package/dist/native/expo/FeedbackStack.d.ts.map +1 -1
- package/dist/native/expo/FeedbackStack.js +10 -5
- package/dist/native/expo/FeedbackStack.js.map +1 -1
- package/dist/native/expo/FeedbackStackLayout.d.ts +3 -0
- package/dist/native/expo/FeedbackStackLayout.d.ts.map +1 -0
- package/dist/native/expo/FeedbackStackLayout.js +54 -0
- package/dist/native/expo/FeedbackStackLayout.js.map +1 -0
- package/dist/native/expo/RoutedFeedbackContext.d.ts +4 -0
- package/dist/native/expo/RoutedFeedbackContext.d.ts.map +1 -0
- package/dist/native/expo/RoutedFeedbackContext.js +11 -0
- package/dist/native/expo/RoutedFeedbackContext.js.map +1 -0
- package/dist/native/expo/RoutedFeedbackModalContext.d.ts +7 -0
- package/dist/native/expo/RoutedFeedbackModalContext.d.ts.map +1 -0
- package/dist/native/expo/RoutedFeedbackModalContext.js +7 -0
- package/dist/native/expo/RoutedFeedbackModalContext.js.map +1 -0
- package/dist/native/expo/index.d.ts +4 -0
- package/dist/native/expo/index.d.ts.map +1 -1
- package/dist/native/expo/index.js +4 -0
- package/dist/native/expo/index.js.map +1 -1
- package/dist/native/expo/routes.d.ts +16 -0
- package/dist/native/expo/routes.d.ts.map +1 -0
- package/dist/native/expo/routes.js +23 -0
- package/dist/native/expo/routes.js.map +1 -0
- package/dist/native/expo/types.d.ts +53 -1
- package/dist/native/expo/types.d.ts.map +1 -1
- package/dist/native/native/FeedbackScreen.d.ts +1 -1
- package/dist/native/native/FeedbackScreen.d.ts.map +1 -1
- package/dist/native/native/FeedbackScreen.js +2 -2
- package/dist/native/native/FeedbackScreen.js.map +1 -1
- package/dist/native/shared/ui/BodyContent.d.ts +1 -1
- package/dist/native/shared/ui/BodyContent.d.ts.map +1 -1
- package/dist/native/shared/ui/BodyContent.js +2 -2
- package/dist/native/shared/ui/BodyContent.js.map +1 -1
- package/dist/native/shared/ui/Button.d.ts +2 -1
- package/dist/native/shared/ui/Button.d.ts.map +1 -1
- package/dist/native/shared/ui/Button.js +9 -4
- package/dist/native/shared/ui/Button.js.map +1 -1
- package/dist/native/shared/ui/FeedbackScreenHeader.d.ts.map +1 -1
- package/dist/native/shared/ui/FeedbackScreenHeader.js +8 -4
- package/dist/native/shared/ui/FeedbackScreenHeader.js.map +1 -1
- package/dist/native/shared/ui/FeedbackScreenList.d.ts +1 -1
- package/dist/native/shared/ui/FeedbackScreenList.d.ts.map +1 -1
- package/dist/native/shared/ui/FeedbackScreenList.js +18 -5
- package/dist/native/shared/ui/FeedbackScreenList.js.map +1 -1
- package/dist/native/shared/ui/NewEntry.d.ts +10 -0
- package/dist/native/shared/ui/NewEntry.d.ts.map +1 -1
- package/dist/native/shared/ui/NewEntry.js +7 -14
- package/dist/native/shared/ui/NewEntry.js.map +1 -1
- package/dist/native/shared/ui/primitives.d.ts +7 -1
- package/dist/native/shared/ui/primitives.d.ts.map +1 -1
- package/dist/native/shared/ui/primitives.js +38 -0
- package/dist/native/shared/ui/primitives.js.map +1 -1
- package/dist/react/primitives.d.ts +9 -1
- package/dist/react/primitives.d.ts.map +1 -1
- package/dist/react/primitives.js +12 -0
- package/dist/react/primitives.js.map +1 -1
- package/dist/react/screen.d.ts +1 -1
- package/dist/react/screen.d.ts.map +1 -1
- package/dist/react/screen.js +21 -10
- package/dist/react/screen.js.map +1 -1
- package/dist/react/styles.css +38 -1
- package/dist/shared/context/FeedbackBodyProvider.d.ts +1 -1
- package/dist/shared/context/FeedbackBodyProvider.d.ts.map +1 -1
- package/dist/shared/context/FeedbackBodyProvider.js +5 -1
- package/dist/shared/context/FeedbackBodyProvider.js.map +1 -1
- package/dist/shared/helpers.d.ts +5 -1
- package/dist/shared/helpers.d.ts.map +1 -1
- package/dist/shared/helpers.js +11 -0
- package/dist/shared/helpers.js.map +1 -1
- package/dist/shared/messages.d.ts.map +1 -1
- package/dist/shared/messages.js +1 -0
- package/dist/shared/messages.js.map +1 -1
- package/dist/shared/types/choice.d.ts +10 -0
- package/dist/shared/types/choice.d.ts.map +1 -0
- package/dist/shared/types/choice.js +2 -0
- package/dist/shared/types/choice.js.map +1 -0
- package/dist/shared/types/index.d.ts +1 -0
- package/dist/shared/types/index.d.ts.map +1 -1
- package/dist/shared/types/messages.d.ts +2 -0
- package/dist/shared/types/messages.d.ts.map +1 -1
- package/dist/shared/types/screen.d.ts +13 -2
- package/dist/shared/types/screen.d.ts.map +1 -1
- package/package.json +12 -8
- package/src/native/expo/ExpoFeedbackBody.tsx +6 -1
- package/src/native/expo/FeedbackCreateStackLayout.tsx +62 -0
- package/src/native/expo/FeedbackRoutes.tsx +238 -0
- package/src/native/expo/FeedbackScreen.tsx +2 -0
- package/src/native/expo/FeedbackStack.tsx +40 -5
- package/src/native/expo/FeedbackStackLayout.tsx +118 -0
- package/src/native/expo/README.md +24 -0
- package/src/native/expo/RoutedFeedbackContext.tsx +20 -0
- package/src/native/expo/RoutedFeedbackModalContext.tsx +14 -0
- package/src/native/expo/index.ts +4 -0
- package/src/native/expo/routes.ts +41 -0
- package/src/native/expo/types.ts +65 -1
- package/src/native/native/FeedbackScreen.tsx +2 -0
- package/src/native/shared/ui/BodyContent.tsx +2 -0
- package/src/native/shared/ui/Button.tsx +12 -3
- package/src/native/shared/ui/FeedbackScreenHeader.tsx +25 -4
- package/src/native/shared/ui/FeedbackScreenList.tsx +48 -8
- package/src/native/shared/ui/NewEntry.tsx +12 -30
- package/src/native/shared/ui/primitives.tsx +90 -0
- package/src/react/primitives.tsx +55 -0
- package/src/react/screen.tsx +67 -33
- package/src/react/styles.css +38 -1
- package/src/shared/context/FeedbackBodyProvider.tsx +6 -0
- package/src/shared/helpers.ts +21 -1
- package/src/shared/messages.ts +1 -0
- package/src/shared/types/choice.ts +10 -0
- package/src/shared/types/index.ts +1 -0
- package/src/shared/types/messages.ts +3 -0
- package/src/shared/types/screen.ts +15 -1
package/src/react/screen.tsx
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
FeedbackProvider,
|
|
12
12
|
useFeedbackUi,
|
|
13
13
|
} from "../shared/context/FeedbackProvider";
|
|
14
|
+
import { createEntryLabel, entryStatusChoices } from "../shared/helpers.js";
|
|
14
15
|
import type {
|
|
15
16
|
FeedbackScreenCommentBranchProps,
|
|
16
17
|
FeedbackScreenContentProps,
|
|
@@ -22,6 +23,7 @@ import type {
|
|
|
22
23
|
} from "../shared/types/";
|
|
23
24
|
import {
|
|
24
25
|
Comment,
|
|
26
|
+
ChoiceChips,
|
|
25
27
|
FeedbackBoard,
|
|
26
28
|
FeedbackEntry,
|
|
27
29
|
FeedbackForm,
|
|
@@ -52,6 +54,7 @@ export function FeedbackScreen({
|
|
|
52
54
|
renderActor,
|
|
53
55
|
debounceDuration = 300,
|
|
54
56
|
collectMetadata,
|
|
57
|
+
emptyState,
|
|
55
58
|
...props
|
|
56
59
|
}: FeedbackScreenProps) {
|
|
57
60
|
return (
|
|
@@ -64,6 +67,7 @@ export function FeedbackScreen({
|
|
|
64
67
|
maxCommentDepth={maxCommentDepth}
|
|
65
68
|
debounceDuration={debounceDuration}
|
|
66
69
|
collectMetadata={collectMetadata}
|
|
70
|
+
emptyState={emptyState}
|
|
67
71
|
collectStandardMetadata={collectWebMetadata}
|
|
68
72
|
transformComments={transformComments}
|
|
69
73
|
renderActor={renderActor}
|
|
@@ -90,16 +94,24 @@ function FeedbackScreenInner({
|
|
|
90
94
|
setShowForm,
|
|
91
95
|
setSelectedEntryId,
|
|
92
96
|
selectedEntryId,
|
|
97
|
+
statusFilter,
|
|
98
|
+
setStatusFilter,
|
|
93
99
|
query,
|
|
94
100
|
setQuery,
|
|
95
101
|
debouncedQuery,
|
|
102
|
+
emptyState,
|
|
96
103
|
} = useFeedbackBody();
|
|
97
104
|
const { messages } = useFeedbackUi();
|
|
98
105
|
|
|
99
|
-
const list = hooks.useEntries({
|
|
106
|
+
const list = hooks.useEntries({
|
|
107
|
+
sort: entrySort,
|
|
108
|
+
kinds: enabledKinds,
|
|
109
|
+
statusFilter,
|
|
110
|
+
});
|
|
100
111
|
const searchResults = hooks.useSearchEntries({
|
|
101
112
|
searchQuery: debouncedQuery,
|
|
102
113
|
kinds: enabledKinds,
|
|
114
|
+
statusFilter,
|
|
103
115
|
});
|
|
104
116
|
|
|
105
117
|
if (selectedEntryId !== null) {
|
|
@@ -125,13 +137,21 @@ function FeedbackScreenInner({
|
|
|
125
137
|
<div>
|
|
126
138
|
<FeedbackBoard.Title />
|
|
127
139
|
<p className="cf-board__subtitle">{messages.board.subtitle}</p>
|
|
140
|
+
<ChoiceChips
|
|
141
|
+
aria-label={messages.board.statusFilter}
|
|
142
|
+
options={entryStatusChoices(messages)}
|
|
143
|
+
value={statusFilter}
|
|
144
|
+
onValueChange={setStatusFilter}
|
|
145
|
+
/>
|
|
128
146
|
</div>
|
|
129
147
|
<button
|
|
130
148
|
type="button"
|
|
131
149
|
className="cf-button cf-button--primary"
|
|
132
150
|
onClick={() => setShowForm((current) => !current)}
|
|
133
151
|
>
|
|
134
|
-
{showForm
|
|
152
|
+
{showForm
|
|
153
|
+
? messages.form.cancel
|
|
154
|
+
: createEntryLabel(enabledKinds, messages)}
|
|
135
155
|
</button>
|
|
136
156
|
</FeedbackBoard.Header>
|
|
137
157
|
|
|
@@ -146,26 +166,39 @@ function FeedbackScreenInner({
|
|
|
146
166
|
|
|
147
167
|
<FeedbackBoard.Search value={query} onValueChange={setQuery} />
|
|
148
168
|
|
|
149
|
-
{loading
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
169
|
+
{loading ? (
|
|
170
|
+
<FeedbackBoard.State>
|
|
171
|
+
<span className="cf-state__message">{messages.board.loading}</span>
|
|
172
|
+
</FeedbackBoard.State>
|
|
173
|
+
) : entries !== undefined && entries.length === 0 ? (
|
|
174
|
+
<FeedbackBoard.State>
|
|
175
|
+
{emptyState}
|
|
176
|
+
<span className="cf-state__message">
|
|
177
|
+
{searching
|
|
178
|
+
? messages.board.noSearchResults
|
|
179
|
+
: messages.board.noEntries}
|
|
180
|
+
</span>
|
|
181
|
+
<button
|
|
182
|
+
type="button"
|
|
183
|
+
className="cf-button cf-button--primary"
|
|
184
|
+
onClick={() => setShowForm(true)}
|
|
185
|
+
>
|
|
186
|
+
{createEntryLabel(enabledKinds, messages)}
|
|
187
|
+
</button>
|
|
188
|
+
</FeedbackBoard.State>
|
|
189
|
+
) : (
|
|
190
|
+
<FeedbackBoard.List>
|
|
191
|
+
{(entries ?? []).map((entry) => (
|
|
192
|
+
<EntryCard
|
|
193
|
+
key={entry.id}
|
|
194
|
+
entry={entry}
|
|
195
|
+
hooks={hooks}
|
|
196
|
+
onOpen={() => setSelectedEntryId(entry.id)}
|
|
197
|
+
/>
|
|
198
|
+
))}
|
|
199
|
+
</FeedbackBoard.List>
|
|
156
200
|
)}
|
|
157
201
|
|
|
158
|
-
<FeedbackBoard.List>
|
|
159
|
-
{(entries ?? []).map((entry) => (
|
|
160
|
-
<EntryCard
|
|
161
|
-
key={entry.id}
|
|
162
|
-
entry={entry}
|
|
163
|
-
hooks={hooks}
|
|
164
|
-
onOpen={() => setSelectedEntryId(entry.id)}
|
|
165
|
-
/>
|
|
166
|
-
))}
|
|
167
|
-
</FeedbackBoard.List>
|
|
168
|
-
|
|
169
202
|
{!searching && list.status === "CanLoadMore" && (
|
|
170
203
|
<button
|
|
171
204
|
type="button"
|
|
@@ -273,19 +306,20 @@ function CreateEntryForm({ onCreated }: FeedbackScreenEntryModalProps) {
|
|
|
273
306
|
|
|
274
307
|
return (
|
|
275
308
|
<FeedbackForm.Root onSubmit={submit}>
|
|
276
|
-
|
|
277
|
-
<
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
309
|
+
{enabledKinds.length > 1 && (
|
|
310
|
+
<label className="cf-field">
|
|
311
|
+
<span>{messages.form.kind}</span>
|
|
312
|
+
<ChoiceChips
|
|
313
|
+
aria-label={messages.form.kind}
|
|
314
|
+
options={enabledKinds.map((value) => ({
|
|
315
|
+
value,
|
|
316
|
+
label: messages.kinds[value],
|
|
317
|
+
}))}
|
|
318
|
+
value={kind}
|
|
319
|
+
onValueChange={setKind}
|
|
320
|
+
/>
|
|
321
|
+
</label>
|
|
322
|
+
)}
|
|
289
323
|
<label className="cf-field">
|
|
290
324
|
<span>{messages.form.title}</span>
|
|
291
325
|
<FeedbackForm.Input
|
package/src/react/styles.css
CHANGED
|
@@ -30,6 +30,33 @@
|
|
|
30
30
|
margin: 0.4rem 0 0;
|
|
31
31
|
color: var(--cf-muted);
|
|
32
32
|
}
|
|
33
|
+
:where(.cf-board) :where(.cf-choice-chips) {
|
|
34
|
+
display: flex;
|
|
35
|
+
flex-wrap: wrap;
|
|
36
|
+
align-items: center;
|
|
37
|
+
gap: 7px;
|
|
38
|
+
margin-top: 0.75rem;
|
|
39
|
+
}
|
|
40
|
+
:where(.cf-board) :where(.cf-choice-chip) {
|
|
41
|
+
border: 1px solid var(--cf-border);
|
|
42
|
+
border-radius: 999px;
|
|
43
|
+
padding: 6px 10px;
|
|
44
|
+
background: var(--cf-surface);
|
|
45
|
+
color: var(--cf-muted);
|
|
46
|
+
font: inherit;
|
|
47
|
+
font-size: 0.85rem;
|
|
48
|
+
font-weight: 600;
|
|
49
|
+
cursor: pointer;
|
|
50
|
+
}
|
|
51
|
+
:where(.cf-board) :where(.cf-choice-chip[data-selected="true"]) {
|
|
52
|
+
border-color: var(--cf-primary);
|
|
53
|
+
background: color-mix(in srgb, var(--cf-primary) 10%, var(--cf-surface));
|
|
54
|
+
color: var(--cf-primary);
|
|
55
|
+
}
|
|
56
|
+
:where(.cf-board) :where(.cf-choice-chip:focus-visible) {
|
|
57
|
+
outline: 3px solid color-mix(in srgb, var(--cf-primary) 25%, transparent);
|
|
58
|
+
outline-offset: 2px;
|
|
59
|
+
}
|
|
33
60
|
:where(.cf-board) :where(.cf-board__list),
|
|
34
61
|
:where(.cf-board) :where(.cf-comments) {
|
|
35
62
|
display: grid;
|
|
@@ -198,8 +225,18 @@
|
|
|
198
225
|
-webkit-box-orient: vertical;
|
|
199
226
|
}
|
|
200
227
|
:where(.cf-board) :where(.cf-state) {
|
|
228
|
+
display: flex;
|
|
229
|
+
min-height: 180px;
|
|
230
|
+
flex-direction: column;
|
|
231
|
+
align-items: center;
|
|
232
|
+
justify-content: center;
|
|
233
|
+
gap: var(--cf-space);
|
|
234
|
+
padding: calc(var(--cf-space) * 1.5);
|
|
201
235
|
color: var(--cf-muted);
|
|
202
|
-
|
|
236
|
+
text-align: center;
|
|
237
|
+
}
|
|
238
|
+
:where(.cf-board) :where(.cf-state__message) {
|
|
239
|
+
margin: 0;
|
|
203
240
|
}
|
|
204
241
|
:where(.cf-board) :where(.cf-detail),
|
|
205
242
|
:where(.cf-board) :where(.cf-discussion) {
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
useMemo,
|
|
7
7
|
useState,
|
|
8
8
|
} from "react";
|
|
9
|
+
import type { EntryStatusFilter } from "convex-feedback";
|
|
9
10
|
import { debounce } from "../helpers";
|
|
10
11
|
import type {
|
|
11
12
|
FeedbackScreenBodyContextValue,
|
|
@@ -23,6 +24,7 @@ export function FeedbackBodyProvider({
|
|
|
23
24
|
maxCommentDepth,
|
|
24
25
|
debounceDuration,
|
|
25
26
|
collectMetadata,
|
|
27
|
+
emptyState,
|
|
26
28
|
collectStandardMetadata,
|
|
27
29
|
transformComments,
|
|
28
30
|
renderActor,
|
|
@@ -33,6 +35,7 @@ export function FeedbackBodyProvider({
|
|
|
33
35
|
const [isSearching, setIsSearching] = useState(false);
|
|
34
36
|
const [query, setQuery] = useState("");
|
|
35
37
|
const [debouncedQuery, setDebouncedQuery] = useState("");
|
|
38
|
+
const [statusFilter, setStatusFilter] = useState<EntryStatusFilter>("open");
|
|
36
39
|
|
|
37
40
|
const updateDebouncedQuery = useMemo(
|
|
38
41
|
() => debounce(setDebouncedQuery, debounceDuration),
|
|
@@ -51,12 +54,14 @@ export function FeedbackBodyProvider({
|
|
|
51
54
|
showForm,
|
|
52
55
|
isSearching,
|
|
53
56
|
selectedEntryId,
|
|
57
|
+
statusFilter,
|
|
54
58
|
|
|
55
59
|
setQuery,
|
|
56
60
|
setDebouncedQuery,
|
|
57
61
|
setShowForm,
|
|
58
62
|
setIsSearching,
|
|
59
63
|
setSelectedEntryId,
|
|
64
|
+
setStatusFilter,
|
|
60
65
|
|
|
61
66
|
hooks,
|
|
62
67
|
entrySort,
|
|
@@ -65,6 +70,7 @@ export function FeedbackBodyProvider({
|
|
|
65
70
|
maxCommentDepth,
|
|
66
71
|
debounceDuration,
|
|
67
72
|
collectMetadata,
|
|
73
|
+
emptyState,
|
|
68
74
|
collectStandardMetadata,
|
|
69
75
|
transformComments,
|
|
70
76
|
renderActor,
|
package/src/shared/helpers.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import type { EntryKind } from "convex-feedback";
|
|
1
|
+
import type { EntryKind, EntryStatusFilter } from "convex-feedback";
|
|
2
|
+
import type { FeedbackMessages } from "./types/messages.js";
|
|
3
|
+
import type { ChoiceChipOption } from "./types/choice.js";
|
|
2
4
|
|
|
3
5
|
export const kinds: readonly EntryKind[] = [
|
|
4
6
|
"feedback",
|
|
@@ -6,6 +8,24 @@ export const kinds: readonly EntryKind[] = [
|
|
|
6
8
|
"bug_report",
|
|
7
9
|
];
|
|
8
10
|
|
|
11
|
+
export function createEntryLabel(
|
|
12
|
+
enabledKinds: readonly EntryKind[],
|
|
13
|
+
messages: FeedbackMessages,
|
|
14
|
+
): string {
|
|
15
|
+
return enabledKinds.length === 1
|
|
16
|
+
? messages.newFeedback[enabledKinds[0]!]
|
|
17
|
+
: messages.board.createEntry;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function entryStatusChoices(
|
|
21
|
+
messages: FeedbackMessages,
|
|
22
|
+
): readonly ChoiceChipOption<EntryStatusFilter>[] {
|
|
23
|
+
return [
|
|
24
|
+
{ value: "open", label: messages.statuses.open },
|
|
25
|
+
{ value: "closed", label: messages.statuses.closed },
|
|
26
|
+
];
|
|
27
|
+
}
|
|
28
|
+
|
|
9
29
|
export function debounce<TArgs extends unknown[]>(
|
|
10
30
|
callback: (...args: TArgs) => void,
|
|
11
31
|
delay: number,
|
package/src/shared/messages.ts
CHANGED
|
@@ -5,6 +5,7 @@ export const englishFeedbackMessages: FeedbackMessages = {
|
|
|
5
5
|
title: "Feedback",
|
|
6
6
|
subtitle: "Share ideas, report problems, and help shape what comes next.",
|
|
7
7
|
searchPlaceholder: "Search feedback…",
|
|
8
|
+
statusFilter: "Filter by status",
|
|
8
9
|
createEntry: "Create feedback",
|
|
9
10
|
noEntries: "No feedback yet.",
|
|
10
11
|
noSearchResults: "No matching feedback found.",
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface ChoiceChipOption<Value extends string = string> {
|
|
2
|
+
value: Value;
|
|
3
|
+
label: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface ChoiceChipsBaseProps<Value extends string = string> {
|
|
7
|
+
options: readonly ChoiceChipOption<Value>[];
|
|
8
|
+
value: Value;
|
|
9
|
+
onValueChange: (value: Value) => void;
|
|
10
|
+
}
|
|
@@ -18,6 +18,9 @@ export interface FeedbackMessages {
|
|
|
18
18
|
/** Search input placeholder. */
|
|
19
19
|
searchPlaceholder: string;
|
|
20
20
|
|
|
21
|
+
/** Accessible label for the open/closed entry status filter. */
|
|
22
|
+
statusFilter: string;
|
|
23
|
+
|
|
21
24
|
/** CTA for opening the create-entry flow.
|
|
22
25
|
* This is shown when enabled kinds are two or more.
|
|
23
26
|
* Use messages.newFeedback for a single-kind entry.
|
|
@@ -2,6 +2,7 @@ import type {
|
|
|
2
2
|
CommentSort,
|
|
3
3
|
EntryKind,
|
|
4
4
|
EntrySort,
|
|
5
|
+
EntryStatusFilter,
|
|
5
6
|
FeedbackComment,
|
|
6
7
|
FeedbackEntry,
|
|
7
8
|
} from "convex-feedback";
|
|
@@ -95,6 +96,9 @@ export interface FeedbackScreenBaseProps {
|
|
|
95
96
|
* entry kind. Disabled by default.
|
|
96
97
|
*/
|
|
97
98
|
collectMetadata?: CollectMetadata;
|
|
99
|
+
|
|
100
|
+
/** Optional host-owned content rendered above the empty-state message. */
|
|
101
|
+
emptyState?: ReactNode;
|
|
98
102
|
}
|
|
99
103
|
|
|
100
104
|
export interface FeedbackScreenRootProps
|
|
@@ -106,9 +110,10 @@ export interface FeedbackScreenRootProps
|
|
|
106
110
|
|
|
107
111
|
export interface FeedbackScreenProviderProps
|
|
108
112
|
extends
|
|
109
|
-
Required<Omit<FeedbackScreenBaseProps, "collectMetadata">>,
|
|
113
|
+
Required<Omit<FeedbackScreenBaseProps, "collectMetadata" | "emptyState">>,
|
|
110
114
|
FeedbackScreenTransformationProps {
|
|
111
115
|
collectMetadata?: CollectMetadata;
|
|
116
|
+
emptyState?: ReactNode;
|
|
112
117
|
collectStandardMetadata: StandardMetadataCollector;
|
|
113
118
|
}
|
|
114
119
|
|
|
@@ -119,11 +124,13 @@ export interface FeedbackScreenBodyContextValue
|
|
|
119
124
|
showForm: boolean;
|
|
120
125
|
isSearching: boolean;
|
|
121
126
|
selectedEntryId: string | null;
|
|
127
|
+
statusFilter: EntryStatusFilter;
|
|
122
128
|
setQuery: React.Dispatch<React.SetStateAction<string>>;
|
|
123
129
|
setDebouncedQuery: React.Dispatch<React.SetStateAction<string>>;
|
|
124
130
|
setShowForm: React.Dispatch<React.SetStateAction<boolean>>;
|
|
125
131
|
setIsSearching: React.Dispatch<React.SetStateAction<boolean>>;
|
|
126
132
|
setSelectedEntryId: React.Dispatch<React.SetStateAction<string | null>>;
|
|
133
|
+
setStatusFilter: React.Dispatch<React.SetStateAction<EntryStatusFilter>>;
|
|
127
134
|
}
|
|
128
135
|
|
|
129
136
|
export type FeedbackScreenBodyProps = FeedbackColorProps;
|
|
@@ -139,6 +146,13 @@ export interface FeedbackScreenListProps {
|
|
|
139
146
|
hideBackButton?: boolean;
|
|
140
147
|
/** Callback when the EntryCard is opened. */
|
|
141
148
|
onEntryOpen: (entryId: string) => void;
|
|
149
|
+
/** Whether provider-selected entries are rendered in place of the list.
|
|
150
|
+
* @default true
|
|
151
|
+
*/
|
|
152
|
+
showSelectedEntry?: boolean;
|
|
153
|
+
|
|
154
|
+
/** Callback for the empty-state create-entry action. */
|
|
155
|
+
onCreateEntry?: () => void;
|
|
142
156
|
}
|
|
143
157
|
|
|
144
158
|
export interface FeedbackScreenContentProps
|