spaps-issue-reporting-react 0.4.0 → 0.4.2
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/CHANGELOG.md +16 -0
- package/README.md +84 -2
- package/dist/index.d.mts +78 -3
- package/dist/index.d.ts +78 -3
- package/dist/index.js +733 -27
- package/dist/index.mjs +731 -28
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [Unreleased]
|
|
4
|
+
|
|
5
|
+
- Added the reporter-visible operator-to-reporter conversation thread (`IssueReportMessageThread`), rendered inside the issue detail modal when the client implements `issueReporting.listMessages`/`submitMessage`. It shows only `active`, reporter-visible projection rows (retracted/superseded/non-visible excluded), distinguishes operator vs reporter authorship and message kind, exposes a `needs_response` affordance plus a floating-entrypoint badge, and includes a clarification-response composer that submits with a client-generated `idempotency_key` and handles `409 ISSUE_REPORT_MESSAGE_CONFLICT`.
|
|
6
|
+
|
|
7
|
+
## [0.4.1] - 2026-05-16
|
|
8
|
+
|
|
9
|
+
- Added optional screenshot attachment UI that uploads private PNG/JPEG/WebP evidence through `issueReporting.uploadAttachment`, validates size/count locally, and submits only backend attachment IDs.
|
|
10
|
+
|
|
11
|
+
## [0.4.0] - 2026-05-11
|
|
12
|
+
|
|
13
|
+
- Maintenance: align the React issue-reporting package version after release automation.
|
|
14
|
+
|
|
15
|
+
## [0.3.0] - 2026-05-11
|
|
16
|
+
|
|
17
|
+
- Maintenance: align the React issue-reporting package version after release automation.
|
|
18
|
+
|
|
3
19
|
## [0.2.0] - 2026-05-09
|
|
4
20
|
|
|
5
21
|
- Added voice input support through SPAPS-minted ElevenLabs realtime tokens.
|
package/README.md
CHANGED
|
@@ -18,6 +18,7 @@ This package targets `Node.js >=18` and React 18+. Voice input uses the package'
|
|
|
18
18
|
| --- | --- |
|
|
19
19
|
| A visible issue-report entry point | Floating button with open/recent state |
|
|
20
20
|
| A guided reporting flow | Page-first create flow, optional section picking, create/edit/reply modal |
|
|
21
|
+
| Screenshot evidence | Optional picker that uploads private PNG/JPEG/WebP attachments through the host client |
|
|
21
22
|
| A lightweight integration contract | Any client that exposes `issueReporting.*` methods |
|
|
22
23
|
| App-level control | Eligibility, reporter identity, scope, page policy, and copy stay in your app |
|
|
23
24
|
|
|
@@ -71,6 +72,12 @@ export function AppShell() {
|
|
|
71
72
|
|
|
72
73
|
- A client with `issueReporting.getStatus`, `list`, `get`, `create`, `update`, and `reply`.
|
|
73
74
|
- A client with `issueReporting.createVoiceToken` when `inputModes` includes `voice`.
|
|
75
|
+
- A client with `issueReporting.uploadAttachment(file, { filename })` when you want screenshot uploads.
|
|
76
|
+
- Optionally, a client with `issueReporting.listMessages(issueReportId)` and
|
|
77
|
+
`issueReporting.submitMessage(issueReportId, { body, idempotency_key })` to wire the
|
|
78
|
+
operator-to-reporter message thread (and its `needs_response` badge). When these are present,
|
|
79
|
+
the bundled `IssueReportMessageThread` renders the conversation inside the issue detail modal
|
|
80
|
+
(see [Conversation Thread](#conversation-thread)).
|
|
74
81
|
- Any auth and token refresh behavior needed by that client.
|
|
75
82
|
- Eligibility rules such as feature flags, account state, or role checks.
|
|
76
83
|
- The current principal ID and optional role hint passed into the provider.
|
|
@@ -78,6 +85,7 @@ export function AppShell() {
|
|
|
78
85
|
supports `mine` only; use `allowTenantScope` only with a client that implements tenant reads.
|
|
79
86
|
- Whether a page defaults to `general_page`, `surface_required`, or `surface_preferred` reporting.
|
|
80
87
|
- Whether the app allows `["text"]`, `["voice"]`, or `["text", "voice"]` report input.
|
|
88
|
+
- User-facing screenshot sensitivity warnings for surfaces that may capture private data.
|
|
81
89
|
- Styling integration if your build strips package utility classes.
|
|
82
90
|
- Any app-specific copy overrides.
|
|
83
91
|
- Origin registration on the owning SPAPS application if the browser calls SPAPS directly with a publishable key.
|
|
@@ -127,18 +135,75 @@ The SPAPS server also needs `ELEVENLABS_API_KEY`. The browser calls your normal
|
|
|
127
135
|
|
|
128
136
|
When text and voice are both enabled, the modal keeps the textarea available and lets the user append a committed transcript into the editable note.
|
|
129
137
|
|
|
138
|
+
## Screenshot Attachments
|
|
139
|
+
|
|
140
|
+
Screenshot attachments are opt-in through the client contract. If the client passed to `IssueReportingProvider` exposes `issueReporting.uploadAttachment`, the modal renders a screenshot picker. If the method is absent, the picker stays hidden and text/voice reporting works unchanged.
|
|
141
|
+
|
|
142
|
+
```tsx
|
|
143
|
+
const client = {
|
|
144
|
+
issueReporting: {
|
|
145
|
+
...spaps.issueReporting,
|
|
146
|
+
uploadAttachment: (file: Blob, options?: { filename?: string }) =>
|
|
147
|
+
spaps.issueReporting.uploadAttachment(file, options),
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
The picker accepts PNG, JPEG, and WebP files, enforces the 10 MiB per-file limit and 5-screenshot report limit, previews pending files, and preserves existing attachments while editing. On submit, it uploads pending files first and sends only attachment IDs through `attachment_ids`, `add_attachment_ids`, or `remove_attachment_ids`.
|
|
153
|
+
|
|
154
|
+
This package does not store screenshots, generate public URLs, or redact image content. SPAPS stores screenshots through its shared hosted-asset boundary as private objects, and access URLs are minted by the backend after authorization. Host apps should warn users before upload when a screenshot might include PHI, credentials, payment data, or other sensitive content.
|
|
155
|
+
|
|
156
|
+
## Conversation Thread
|
|
157
|
+
|
|
158
|
+
Support operators can ask a reporter for clarification and post a final resolution message. Those messages flow into an allowlisted, reporter-visible projection that the reporter reads back as a conversation. This package renders that surface when the client implements the two optional message methods:
|
|
159
|
+
|
|
160
|
+
```tsx
|
|
161
|
+
const client = {
|
|
162
|
+
issueReporting: {
|
|
163
|
+
...spaps.issueReporting,
|
|
164
|
+
listMessages: (issueReportId: string) =>
|
|
165
|
+
spaps.issueReporting.listMessages(issueReportId),
|
|
166
|
+
submitMessage: (issueReportId, payload) =>
|
|
167
|
+
spaps.issueReporting.submitMessage(issueReportId, payload),
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
When `listMessages` is present, `FloatingIssueReportButton` mounts the exported `IssueReportMessageThread` inside the issue detail modal (the Edit/Reply views). The thread:
|
|
173
|
+
|
|
174
|
+
- Renders only `active`, `reporter_visible` projection rows in chronological order. Retracted and superseded operator messages, non-visible rows, and raw `support_case_events` payloads are never displayed. The backend filters this; `selectReporterVisibleMessages` re-asserts the same rule in the UI.
|
|
175
|
+
- Distinguishes operator (`Support`) vs reporter (`You`) authorship, labels each message kind (`clarification_request`, `reporter_response`, `final_response`), and shows a relative timestamp.
|
|
176
|
+
- Shows a **needs-response** affordance when the API's `needs_response` flag is set (an open clarification request awaiting an answer), and surfaces a matching dot badge on the floating entrypoint while that thread is open.
|
|
177
|
+
- Renders a reporter clarification-response composer (shown only when the client implements `submitMessage`). It calls `submitMessage(issueReportId, { body, idempotency_key })` with a client-generated `idempotency_key`. A key reused with a different body returns `409 ISSUE_REPORT_MESSAGE_CONFLICT`; the composer detects this (`isReporterMessageConflict`), shows the conflict copy, and rotates the key for retry.
|
|
178
|
+
- Exposes loading, empty, and error (with retry) states.
|
|
179
|
+
|
|
180
|
+
The composer does not reopen a closed case; reopen semantics stay with the Reply flow. You can also mount `IssueReportMessageThread` directly (outside the modal) inside your own issue detail view:
|
|
181
|
+
|
|
182
|
+
```tsx
|
|
183
|
+
import { IssueReportMessageThread } from "spaps-issue-reporting-react";
|
|
184
|
+
|
|
185
|
+
<IssueReportMessageThread issueReportId={issue.id} />;
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
All thread strings are overridable through the provider `copy` prop. The thread copy keys are: `threadTitle`, `threadDescription`, `threadLoading`, `threadLoadFailed`, `threadEmpty`, `threadNeedsResponseBadge`, `threadAuthorOperator`, `threadAuthorReporter`, `threadKindClarificationRequest`, `threadKindReporterResponse`, `threadKindFinalResponse`, `threadResponseLabel`, `threadResponsePlaceholder`, `threadResponseSubmitAction`, `threadResponseSubmittingAction`, `threadResponseConflict`, and `threadResponseFailed`.
|
|
189
|
+
|
|
130
190
|
## Exported Surface
|
|
131
191
|
|
|
132
192
|
| Export | Purpose |
|
|
133
193
|
| --- | --- |
|
|
134
194
|
| `IssueReportingProvider` | Owns queries, modal state, copy, scope, and report-mode behavior |
|
|
135
195
|
| `IssueReportingPageConfig` | Per-page override for `general_page`, `surface_required`, or `surface_preferred` create policy |
|
|
136
|
-
| `FloatingIssueReportButton` | Renders the floating entry point, popover, and
|
|
196
|
+
| `FloatingIssueReportButton` | Renders the floating entry point, popover, modal, and (when the client supports messages) the conversation thread |
|
|
197
|
+
| `IssueReportMessageThread` | Renders the reporter-visible operator-to-reporter conversation for one issue report, with a clarification-response composer |
|
|
137
198
|
| `ReportableSection` | Marks a region as selectable when report mode is active |
|
|
138
199
|
| `useIssueReporting` | Full provider state, including `startNewIssue()`, `openPageIssueModal()`, and `enterReportMode()` |
|
|
139
200
|
| `useIssueReportingStatus` | Query helper for summary state |
|
|
140
201
|
| `useIssueReportingHistory` | Query helper for filtered history lists |
|
|
141
202
|
| `useIssueReportingMutations` | Mutation helpers for create, update, and reply flows |
|
|
203
|
+
| `useIssueReportingMessages` | Query helper for one issue report's reporter-visible message thread |
|
|
204
|
+
| `useIssueReportingMessageMutation` | Mutation helper for submitting a reporter clarification response |
|
|
205
|
+
| `selectReporterVisibleMessages` | Filters a message list to `active`, reporter-visible rows in chronological order |
|
|
206
|
+
| `isReporterMessageConflict` | Detects a `409 ISSUE_REPORT_MESSAGE_CONFLICT` from the client transport |
|
|
142
207
|
| `useReportMode` | Low-level selection state for custom wrappers |
|
|
143
208
|
|
|
144
209
|
## Styling Notes
|
|
@@ -192,6 +257,14 @@ Pass `inputModes={["voice"]}` or `inputModes={["text", "voice"]}` to the provide
|
|
|
192
257
|
|
|
193
258
|
Confirm that the SPAPS server has `ELEVENLABS_API_KEY` set and that the current user is authenticated and eligible for issue reporting.
|
|
194
259
|
|
|
260
|
+
### Screenshot controls do not appear
|
|
261
|
+
|
|
262
|
+
Expose `issueReporting.uploadAttachment` on the client passed to `IssueReportingProvider`. The package intentionally hides the picker when the upload helper is absent.
|
|
263
|
+
|
|
264
|
+
### The conversation thread does not appear
|
|
265
|
+
|
|
266
|
+
Expose `issueReporting.listMessages` on the client passed to `IssueReportingProvider`, then open an existing issue's detail (Edit/Reply). The thread is intentionally hidden when the client cannot list messages. The response composer additionally requires `issueReporting.submitMessage`; without it the thread renders read-only.
|
|
267
|
+
|
|
195
268
|
## Limitations
|
|
196
269
|
|
|
197
270
|
- This package assumes React Query is already part of the host app.
|
|
@@ -228,10 +301,19 @@ No. It only renders scope choices that your app explicitly allows.
|
|
|
228
301
|
|
|
229
302
|
No. This package is UI only. If your browser app calls SPAPS directly with a publishable key, the relevant SPAPS application row must include that browser origin in `allowed_origins`.
|
|
230
303
|
|
|
304
|
+
## Design system
|
|
305
|
+
|
|
306
|
+
This package is intentionally theme-neutral and uses accessible Radix behavior
|
|
307
|
+
primitives plus a restrained slate palette so it blends into the host app. The
|
|
308
|
+
Design System Registry (DSR) adoption decision for this surface is an **explicit
|
|
309
|
+
deferral** because no suitable theme-neutral registry item exists yet. The
|
|
310
|
+
repository-level `docs/DSR-DECISION.md` captures the reasoning, file-level audit,
|
|
311
|
+
and revisit trigger.
|
|
312
|
+
|
|
231
313
|
## Metadata
|
|
232
314
|
|
|
233
315
|
- `package_name`: `spaps-issue-reporting-react`
|
|
234
|
-
- `latest_version`: `0.
|
|
316
|
+
- `latest_version`: `0.4.1`
|
|
235
317
|
- `minimum_runtime`: `Node.js >=18.0.0`
|
|
236
318
|
- `api_base_url`: `https://api.sweetpotato.dev`
|
|
237
319
|
|
package/dist/index.d.mts
CHANGED
|
@@ -2,8 +2,8 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import React__default, { ReactNode } from 'react';
|
|
4
4
|
import * as spaps_types from 'spaps-types';
|
|
5
|
-
import { IssueReportScope, IssueReportStatusResult, IssueReportStatus, IssueReportListResult, IssueReport, CreateIssueReportRequest, UpdateIssueReportRequest, ReplyIssueReportRequest, IssueReportingVoiceTokenResult, IssueReportingInputMode, IssueReportingVoiceProvider } from 'spaps-types';
|
|
6
|
-
export { CreateIssueReportRequest, IssueReport, IssueReportListResult, IssueReportScope, IssueReportStatus, IssueReportStatusResult, ReplyIssueReportRequest, UpdateIssueReportRequest } from 'spaps-types';
|
|
5
|
+
import { IssueReportScope, IssueReportStatusResult, IssueReportStatus, IssueReportListResult, IssueReport, CreateIssueReportRequest, UpdateIssueReportRequest, ReplyIssueReportRequest, IssueReportingVoiceTokenResult, IssueReportAttachmentOut, ListIssueReportMessagesResponse, CreateReporterMessageRequest, IssueReportMessage, IssueReportingInputMode, IssueReportingVoiceProvider } from 'spaps-types';
|
|
6
|
+
export { CreateIssueReportRequest, CreateReporterMessageRequest, IssueReport, IssueReportAttachmentOut, IssueReportListResult, IssueReportMessage, IssueReportScope, IssueReportStatus, IssueReportStatusResult, ListIssueReportMessagesResponse, ReplyIssueReportRequest, UpdateIssueReportRequest } from 'spaps-types';
|
|
7
7
|
import * as _tanstack_query_core from '@tanstack/query-core';
|
|
8
8
|
import * as _tanstack_react_query from '@tanstack/react-query';
|
|
9
9
|
|
|
@@ -36,6 +36,11 @@ interface IssueReportingClient {
|
|
|
36
36
|
update: (issueReportId: string, payload: UpdateIssueReportRequest) => Promise<IssueReport>;
|
|
37
37
|
reply: (issueReportId: string, payload: ReplyIssueReportRequest) => Promise<IssueReport>;
|
|
38
38
|
createVoiceToken?: () => Promise<IssueReportingVoiceTokenResult>;
|
|
39
|
+
uploadAttachment?: (file: Blob, options?: {
|
|
40
|
+
filename?: string;
|
|
41
|
+
}) => Promise<IssueReportAttachmentOut>;
|
|
42
|
+
listMessages?: (issueReportId: string) => Promise<ListIssueReportMessagesResponse>;
|
|
43
|
+
submitMessage?: (issueReportId: string, payload: CreateReporterMessageRequest) => Promise<IssueReportMessage>;
|
|
39
44
|
};
|
|
40
45
|
}
|
|
41
46
|
interface ReportableTargetDescriptor {
|
|
@@ -93,6 +98,23 @@ interface IssueReportingCopy {
|
|
|
93
98
|
originHumanLabel: string;
|
|
94
99
|
originMachineLabel: string;
|
|
95
100
|
machineOriginFallback: string;
|
|
101
|
+
threadTitle: string;
|
|
102
|
+
threadDescription: string;
|
|
103
|
+
threadLoading: string;
|
|
104
|
+
threadLoadFailed: string;
|
|
105
|
+
threadEmpty: string;
|
|
106
|
+
threadNeedsResponseBadge: string;
|
|
107
|
+
threadAuthorOperator: string;
|
|
108
|
+
threadAuthorReporter: string;
|
|
109
|
+
threadKindClarificationRequest: string;
|
|
110
|
+
threadKindReporterResponse: string;
|
|
111
|
+
threadKindFinalResponse: string;
|
|
112
|
+
threadResponsePlaceholder: string;
|
|
113
|
+
threadResponseLabel: string;
|
|
114
|
+
threadResponseSubmitAction: string;
|
|
115
|
+
threadResponseSubmittingAction: string;
|
|
116
|
+
threadResponseConflict: string;
|
|
117
|
+
threadResponseFailed: string;
|
|
96
118
|
}
|
|
97
119
|
interface IssueReportingProviderProps {
|
|
98
120
|
client: IssueReportingClient;
|
|
@@ -117,6 +139,9 @@ interface IssueReportingPageConfigProps {
|
|
|
117
139
|
createMode: IssueReportingCreateMode;
|
|
118
140
|
}
|
|
119
141
|
|
|
142
|
+
declare function IssueReportMessageThread({ issueReportId, }: {
|
|
143
|
+
issueReportId: string;
|
|
144
|
+
}): react_jsx_runtime.JSX.Element | null;
|
|
120
145
|
declare function FloatingIssueReportButton({ className, positionClassName, }: FloatingIssueReportButtonProps): react_jsx_runtime.JSX.Element | null;
|
|
121
146
|
declare function ReportableSection({ reportableName, children, className, as: Component, }: {
|
|
122
147
|
reportableName: ReportableInput;
|
|
@@ -171,6 +196,8 @@ type IssueReportingContextValue = {
|
|
|
171
196
|
voice: Required<Pick<IssueReportingVoiceConfig, "provider" | "modelId" | "requireMicrophonePermission">> & {
|
|
172
197
|
microphone?: IssueReportingVoiceConfig["microphone"];
|
|
173
198
|
};
|
|
199
|
+
needsResponseIssueIds: string[];
|
|
200
|
+
setNeedsResponse: (issueReportId: string, needsResponse: boolean) => void;
|
|
174
201
|
};
|
|
175
202
|
declare const defaultIssueReportingCopy: IssueReportingCopy;
|
|
176
203
|
declare const issueReportingKeys: {
|
|
@@ -178,6 +205,7 @@ declare const issueReportingKeys: {
|
|
|
178
205
|
status: (scope: IssueReportScope) => readonly ["spaps-issue-reporting", "status", IssueReportScope];
|
|
179
206
|
history: (scope: IssueReportScope) => readonly ["spaps-issue-reporting", "history", IssueReportScope];
|
|
180
207
|
detail: (issueReportId: string) => readonly ["spaps-issue-reporting", "detail", string];
|
|
208
|
+
messages: (issueReportId: string) => readonly ["spaps-issue-reporting", "messages", string];
|
|
181
209
|
};
|
|
182
210
|
declare function isOpenIssueStatus(status: IssueReportStatus): boolean;
|
|
183
211
|
declare function isClosedIssueStatus(status: IssueReportStatus): boolean;
|
|
@@ -189,6 +217,25 @@ declare function getEntryPointState(status?: {
|
|
|
189
217
|
has_recent_resolved: boolean;
|
|
190
218
|
} | null): "open" | "recent_resolved" | "neutral";
|
|
191
219
|
declare function getEntryPointClassName(state: "open" | "recent_resolved" | "neutral"): string;
|
|
220
|
+
/**
|
|
221
|
+
* Defensive reporter-visibility filter for the conversation surface.
|
|
222
|
+
*
|
|
223
|
+
* The reporter `GET /messages` endpoint already returns only `active`,
|
|
224
|
+
* reporter-visible projection rows (retracted/superseded operator messages and
|
|
225
|
+
* raw `support_case_events` payloads are excluded server-side). This filter
|
|
226
|
+
* re-asserts that contract in the UI so a non-allowlisted row can never be
|
|
227
|
+
* rendered to a reporter even if a client or fixture passes one through. Rows
|
|
228
|
+
* are returned in chronological (`created_at`) order.
|
|
229
|
+
*/
|
|
230
|
+
declare function selectReporterVisibleMessages(messages: IssueReportMessage[]): IssueReportMessage[];
|
|
231
|
+
/**
|
|
232
|
+
* Detect the idempotency-key conflict surfaced by the reporter message API as
|
|
233
|
+
* `409 ISSUE_REPORT_MESSAGE_CONFLICT`. The provider only depends on the
|
|
234
|
+
* abstract `IssueReportingClient` contract, so this inspects whatever shape the
|
|
235
|
+
* host client throws (Error message, `code`, or HTTP `status`) without binding
|
|
236
|
+
* to a specific transport.
|
|
237
|
+
*/
|
|
238
|
+
declare function isReporterMessageConflict(error: unknown): boolean;
|
|
192
239
|
declare function getIssueNoteLengthMessage(note: string, copy: IssueReportingCopy): string;
|
|
193
240
|
declare function useIssueReporting(): IssueReportingContextValue;
|
|
194
241
|
declare function IssueReportingPageConfig({ createMode, }: IssueReportingPageConfigProps): react_jsx_runtime.JSX.Element;
|
|
@@ -373,17 +420,45 @@ declare function useIssueReportingMutations(): {
|
|
|
373
420
|
target: ResolvedTarget;
|
|
374
421
|
note: string;
|
|
375
422
|
reporter_role_hint?: string;
|
|
423
|
+
attachment_ids?: string[];
|
|
376
424
|
}, unknown>;
|
|
377
425
|
updateMutation: _tanstack_react_query.UseMutationResult<IssueReport, Error, {
|
|
378
426
|
issueReportId: string;
|
|
379
427
|
note: string;
|
|
428
|
+
add_attachment_ids?: string[];
|
|
429
|
+
remove_attachment_ids?: string[];
|
|
380
430
|
}, unknown>;
|
|
381
431
|
replyMutation: _tanstack_react_query.UseMutationResult<IssueReport, Error, {
|
|
382
432
|
issueReportId: string;
|
|
383
433
|
note: string;
|
|
384
434
|
reporterRoleHint?: string;
|
|
435
|
+
attachment_ids?: string[];
|
|
385
436
|
}, unknown>;
|
|
386
437
|
};
|
|
438
|
+
/**
|
|
439
|
+
* Query helper for the reporter-visible message thread of one issue report.
|
|
440
|
+
*
|
|
441
|
+
* Keyed by `issue_report_id`. Enabled only when the caller is eligible, an
|
|
442
|
+
* issue id is provided, and the host client implements the optional
|
|
443
|
+
* `listMessages` contract method. The query returns the raw server response so
|
|
444
|
+
* consumers can read `needs_response` for the badge; rendering code should pass
|
|
445
|
+
* `data.items` through {@link selectReporterVisibleMessages} before display.
|
|
446
|
+
*/
|
|
447
|
+
declare function useIssueReportingMessages(issueReportId: string | null): _tanstack_react_query.UseQueryResult<ListIssueReportMessagesResponse, Error>;
|
|
448
|
+
/**
|
|
449
|
+
* Mutation helper for submitting a reporter clarification response.
|
|
450
|
+
*
|
|
451
|
+
* Calls the host client's `submitMessage(issueReportId, { body, idempotency_key })`
|
|
452
|
+
* and, on success, invalidates the thread query so the new `reporter_response`
|
|
453
|
+
* row and refreshed `needs_response` flag are re-fetched. The caller supplies a
|
|
454
|
+
* client-generated `idempotency_key` for safe retries; a key reused with a
|
|
455
|
+
* different body surfaces as a `409` conflict the UI can detect with
|
|
456
|
+
* {@link isReporterMessageConflict}.
|
|
457
|
+
*/
|
|
458
|
+
declare function useIssueReportingMessageMutation(issueReportId: string | null): _tanstack_react_query.UseMutationResult<IssueReportMessage, unknown, {
|
|
459
|
+
body: string;
|
|
460
|
+
idempotency_key?: string;
|
|
461
|
+
}, unknown>;
|
|
387
462
|
declare function IssueReportingProvider({ client, isEligible, reporterRoleHint, principalId, getPageUrl, defaultScope, allowTenantScope, defaultCreateMode, inputModes, defaultInputMode, voice, copy, children, }: IssueReportingProviderProps): react_jsx_runtime.JSX.Element;
|
|
388
463
|
|
|
389
464
|
interface ReportModeContextValue {
|
|
@@ -397,4 +472,4 @@ interface ReportModeContextValue {
|
|
|
397
472
|
declare const ReportModeContext: React.Context<ReportModeContextValue | null>;
|
|
398
473
|
declare function useReportMode(): ReportModeContextValue | null;
|
|
399
474
|
|
|
400
|
-
export { FloatingIssueReportButton, type FloatingIssueReportButtonProps, type IssueHistoryFilter, type IssueReportingClient, type IssueReportingCopy, type IssueReportingCreateMode, IssueReportingPageConfig, type IssueReportingPageConfigProps, IssueReportingProvider, type IssueReportingProviderProps, ReportModeContext, type ReportModeContextValue, type ReportableInput, ReportableSection, type ReportableTargetDescriptor, defaultIssueReportingCopy, filterIssueReports, getEntryPointClassName, getEntryPointState, getIssueNoteLengthMessage, getIssueStatusBadgeLabel, getIssueStatusClassName, isClosedIssueStatus, isOpenIssueStatus, issueReportingKeys, useIssueReporting, useIssueReportingHistory, useIssueReportingMutations, useIssueReportingStatus, useReportMode };
|
|
475
|
+
export { FloatingIssueReportButton, type FloatingIssueReportButtonProps, type IssueHistoryFilter, IssueReportMessageThread, type IssueReportingClient, type IssueReportingCopy, type IssueReportingCreateMode, IssueReportingPageConfig, type IssueReportingPageConfigProps, IssueReportingProvider, type IssueReportingProviderProps, ReportModeContext, type ReportModeContextValue, type ReportableInput, ReportableSection, type ReportableTargetDescriptor, defaultIssueReportingCopy, filterIssueReports, getEntryPointClassName, getEntryPointState, getIssueNoteLengthMessage, getIssueStatusBadgeLabel, getIssueStatusClassName, isClosedIssueStatus, isOpenIssueStatus, isReporterMessageConflict, issueReportingKeys, selectReporterVisibleMessages, useIssueReporting, useIssueReportingHistory, useIssueReportingMessageMutation, useIssueReportingMessages, useIssueReportingMutations, useIssueReportingStatus, useReportMode };
|
package/dist/index.d.ts
CHANGED
|
@@ -2,8 +2,8 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import React__default, { ReactNode } from 'react';
|
|
4
4
|
import * as spaps_types from 'spaps-types';
|
|
5
|
-
import { IssueReportScope, IssueReportStatusResult, IssueReportStatus, IssueReportListResult, IssueReport, CreateIssueReportRequest, UpdateIssueReportRequest, ReplyIssueReportRequest, IssueReportingVoiceTokenResult, IssueReportingInputMode, IssueReportingVoiceProvider } from 'spaps-types';
|
|
6
|
-
export { CreateIssueReportRequest, IssueReport, IssueReportListResult, IssueReportScope, IssueReportStatus, IssueReportStatusResult, ReplyIssueReportRequest, UpdateIssueReportRequest } from 'spaps-types';
|
|
5
|
+
import { IssueReportScope, IssueReportStatusResult, IssueReportStatus, IssueReportListResult, IssueReport, CreateIssueReportRequest, UpdateIssueReportRequest, ReplyIssueReportRequest, IssueReportingVoiceTokenResult, IssueReportAttachmentOut, ListIssueReportMessagesResponse, CreateReporterMessageRequest, IssueReportMessage, IssueReportingInputMode, IssueReportingVoiceProvider } from 'spaps-types';
|
|
6
|
+
export { CreateIssueReportRequest, CreateReporterMessageRequest, IssueReport, IssueReportAttachmentOut, IssueReportListResult, IssueReportMessage, IssueReportScope, IssueReportStatus, IssueReportStatusResult, ListIssueReportMessagesResponse, ReplyIssueReportRequest, UpdateIssueReportRequest } from 'spaps-types';
|
|
7
7
|
import * as _tanstack_query_core from '@tanstack/query-core';
|
|
8
8
|
import * as _tanstack_react_query from '@tanstack/react-query';
|
|
9
9
|
|
|
@@ -36,6 +36,11 @@ interface IssueReportingClient {
|
|
|
36
36
|
update: (issueReportId: string, payload: UpdateIssueReportRequest) => Promise<IssueReport>;
|
|
37
37
|
reply: (issueReportId: string, payload: ReplyIssueReportRequest) => Promise<IssueReport>;
|
|
38
38
|
createVoiceToken?: () => Promise<IssueReportingVoiceTokenResult>;
|
|
39
|
+
uploadAttachment?: (file: Blob, options?: {
|
|
40
|
+
filename?: string;
|
|
41
|
+
}) => Promise<IssueReportAttachmentOut>;
|
|
42
|
+
listMessages?: (issueReportId: string) => Promise<ListIssueReportMessagesResponse>;
|
|
43
|
+
submitMessage?: (issueReportId: string, payload: CreateReporterMessageRequest) => Promise<IssueReportMessage>;
|
|
39
44
|
};
|
|
40
45
|
}
|
|
41
46
|
interface ReportableTargetDescriptor {
|
|
@@ -93,6 +98,23 @@ interface IssueReportingCopy {
|
|
|
93
98
|
originHumanLabel: string;
|
|
94
99
|
originMachineLabel: string;
|
|
95
100
|
machineOriginFallback: string;
|
|
101
|
+
threadTitle: string;
|
|
102
|
+
threadDescription: string;
|
|
103
|
+
threadLoading: string;
|
|
104
|
+
threadLoadFailed: string;
|
|
105
|
+
threadEmpty: string;
|
|
106
|
+
threadNeedsResponseBadge: string;
|
|
107
|
+
threadAuthorOperator: string;
|
|
108
|
+
threadAuthorReporter: string;
|
|
109
|
+
threadKindClarificationRequest: string;
|
|
110
|
+
threadKindReporterResponse: string;
|
|
111
|
+
threadKindFinalResponse: string;
|
|
112
|
+
threadResponsePlaceholder: string;
|
|
113
|
+
threadResponseLabel: string;
|
|
114
|
+
threadResponseSubmitAction: string;
|
|
115
|
+
threadResponseSubmittingAction: string;
|
|
116
|
+
threadResponseConflict: string;
|
|
117
|
+
threadResponseFailed: string;
|
|
96
118
|
}
|
|
97
119
|
interface IssueReportingProviderProps {
|
|
98
120
|
client: IssueReportingClient;
|
|
@@ -117,6 +139,9 @@ interface IssueReportingPageConfigProps {
|
|
|
117
139
|
createMode: IssueReportingCreateMode;
|
|
118
140
|
}
|
|
119
141
|
|
|
142
|
+
declare function IssueReportMessageThread({ issueReportId, }: {
|
|
143
|
+
issueReportId: string;
|
|
144
|
+
}): react_jsx_runtime.JSX.Element | null;
|
|
120
145
|
declare function FloatingIssueReportButton({ className, positionClassName, }: FloatingIssueReportButtonProps): react_jsx_runtime.JSX.Element | null;
|
|
121
146
|
declare function ReportableSection({ reportableName, children, className, as: Component, }: {
|
|
122
147
|
reportableName: ReportableInput;
|
|
@@ -171,6 +196,8 @@ type IssueReportingContextValue = {
|
|
|
171
196
|
voice: Required<Pick<IssueReportingVoiceConfig, "provider" | "modelId" | "requireMicrophonePermission">> & {
|
|
172
197
|
microphone?: IssueReportingVoiceConfig["microphone"];
|
|
173
198
|
};
|
|
199
|
+
needsResponseIssueIds: string[];
|
|
200
|
+
setNeedsResponse: (issueReportId: string, needsResponse: boolean) => void;
|
|
174
201
|
};
|
|
175
202
|
declare const defaultIssueReportingCopy: IssueReportingCopy;
|
|
176
203
|
declare const issueReportingKeys: {
|
|
@@ -178,6 +205,7 @@ declare const issueReportingKeys: {
|
|
|
178
205
|
status: (scope: IssueReportScope) => readonly ["spaps-issue-reporting", "status", IssueReportScope];
|
|
179
206
|
history: (scope: IssueReportScope) => readonly ["spaps-issue-reporting", "history", IssueReportScope];
|
|
180
207
|
detail: (issueReportId: string) => readonly ["spaps-issue-reporting", "detail", string];
|
|
208
|
+
messages: (issueReportId: string) => readonly ["spaps-issue-reporting", "messages", string];
|
|
181
209
|
};
|
|
182
210
|
declare function isOpenIssueStatus(status: IssueReportStatus): boolean;
|
|
183
211
|
declare function isClosedIssueStatus(status: IssueReportStatus): boolean;
|
|
@@ -189,6 +217,25 @@ declare function getEntryPointState(status?: {
|
|
|
189
217
|
has_recent_resolved: boolean;
|
|
190
218
|
} | null): "open" | "recent_resolved" | "neutral";
|
|
191
219
|
declare function getEntryPointClassName(state: "open" | "recent_resolved" | "neutral"): string;
|
|
220
|
+
/**
|
|
221
|
+
* Defensive reporter-visibility filter for the conversation surface.
|
|
222
|
+
*
|
|
223
|
+
* The reporter `GET /messages` endpoint already returns only `active`,
|
|
224
|
+
* reporter-visible projection rows (retracted/superseded operator messages and
|
|
225
|
+
* raw `support_case_events` payloads are excluded server-side). This filter
|
|
226
|
+
* re-asserts that contract in the UI so a non-allowlisted row can never be
|
|
227
|
+
* rendered to a reporter even if a client or fixture passes one through. Rows
|
|
228
|
+
* are returned in chronological (`created_at`) order.
|
|
229
|
+
*/
|
|
230
|
+
declare function selectReporterVisibleMessages(messages: IssueReportMessage[]): IssueReportMessage[];
|
|
231
|
+
/**
|
|
232
|
+
* Detect the idempotency-key conflict surfaced by the reporter message API as
|
|
233
|
+
* `409 ISSUE_REPORT_MESSAGE_CONFLICT`. The provider only depends on the
|
|
234
|
+
* abstract `IssueReportingClient` contract, so this inspects whatever shape the
|
|
235
|
+
* host client throws (Error message, `code`, or HTTP `status`) without binding
|
|
236
|
+
* to a specific transport.
|
|
237
|
+
*/
|
|
238
|
+
declare function isReporterMessageConflict(error: unknown): boolean;
|
|
192
239
|
declare function getIssueNoteLengthMessage(note: string, copy: IssueReportingCopy): string;
|
|
193
240
|
declare function useIssueReporting(): IssueReportingContextValue;
|
|
194
241
|
declare function IssueReportingPageConfig({ createMode, }: IssueReportingPageConfigProps): react_jsx_runtime.JSX.Element;
|
|
@@ -373,17 +420,45 @@ declare function useIssueReportingMutations(): {
|
|
|
373
420
|
target: ResolvedTarget;
|
|
374
421
|
note: string;
|
|
375
422
|
reporter_role_hint?: string;
|
|
423
|
+
attachment_ids?: string[];
|
|
376
424
|
}, unknown>;
|
|
377
425
|
updateMutation: _tanstack_react_query.UseMutationResult<IssueReport, Error, {
|
|
378
426
|
issueReportId: string;
|
|
379
427
|
note: string;
|
|
428
|
+
add_attachment_ids?: string[];
|
|
429
|
+
remove_attachment_ids?: string[];
|
|
380
430
|
}, unknown>;
|
|
381
431
|
replyMutation: _tanstack_react_query.UseMutationResult<IssueReport, Error, {
|
|
382
432
|
issueReportId: string;
|
|
383
433
|
note: string;
|
|
384
434
|
reporterRoleHint?: string;
|
|
435
|
+
attachment_ids?: string[];
|
|
385
436
|
}, unknown>;
|
|
386
437
|
};
|
|
438
|
+
/**
|
|
439
|
+
* Query helper for the reporter-visible message thread of one issue report.
|
|
440
|
+
*
|
|
441
|
+
* Keyed by `issue_report_id`. Enabled only when the caller is eligible, an
|
|
442
|
+
* issue id is provided, and the host client implements the optional
|
|
443
|
+
* `listMessages` contract method. The query returns the raw server response so
|
|
444
|
+
* consumers can read `needs_response` for the badge; rendering code should pass
|
|
445
|
+
* `data.items` through {@link selectReporterVisibleMessages} before display.
|
|
446
|
+
*/
|
|
447
|
+
declare function useIssueReportingMessages(issueReportId: string | null): _tanstack_react_query.UseQueryResult<ListIssueReportMessagesResponse, Error>;
|
|
448
|
+
/**
|
|
449
|
+
* Mutation helper for submitting a reporter clarification response.
|
|
450
|
+
*
|
|
451
|
+
* Calls the host client's `submitMessage(issueReportId, { body, idempotency_key })`
|
|
452
|
+
* and, on success, invalidates the thread query so the new `reporter_response`
|
|
453
|
+
* row and refreshed `needs_response` flag are re-fetched. The caller supplies a
|
|
454
|
+
* client-generated `idempotency_key` for safe retries; a key reused with a
|
|
455
|
+
* different body surfaces as a `409` conflict the UI can detect with
|
|
456
|
+
* {@link isReporterMessageConflict}.
|
|
457
|
+
*/
|
|
458
|
+
declare function useIssueReportingMessageMutation(issueReportId: string | null): _tanstack_react_query.UseMutationResult<IssueReportMessage, unknown, {
|
|
459
|
+
body: string;
|
|
460
|
+
idempotency_key?: string;
|
|
461
|
+
}, unknown>;
|
|
387
462
|
declare function IssueReportingProvider({ client, isEligible, reporterRoleHint, principalId, getPageUrl, defaultScope, allowTenantScope, defaultCreateMode, inputModes, defaultInputMode, voice, copy, children, }: IssueReportingProviderProps): react_jsx_runtime.JSX.Element;
|
|
388
463
|
|
|
389
464
|
interface ReportModeContextValue {
|
|
@@ -397,4 +472,4 @@ interface ReportModeContextValue {
|
|
|
397
472
|
declare const ReportModeContext: React.Context<ReportModeContextValue | null>;
|
|
398
473
|
declare function useReportMode(): ReportModeContextValue | null;
|
|
399
474
|
|
|
400
|
-
export { FloatingIssueReportButton, type FloatingIssueReportButtonProps, type IssueHistoryFilter, type IssueReportingClient, type IssueReportingCopy, type IssueReportingCreateMode, IssueReportingPageConfig, type IssueReportingPageConfigProps, IssueReportingProvider, type IssueReportingProviderProps, ReportModeContext, type ReportModeContextValue, type ReportableInput, ReportableSection, type ReportableTargetDescriptor, defaultIssueReportingCopy, filterIssueReports, getEntryPointClassName, getEntryPointState, getIssueNoteLengthMessage, getIssueStatusBadgeLabel, getIssueStatusClassName, isClosedIssueStatus, isOpenIssueStatus, issueReportingKeys, useIssueReporting, useIssueReportingHistory, useIssueReportingMutations, useIssueReportingStatus, useReportMode };
|
|
475
|
+
export { FloatingIssueReportButton, type FloatingIssueReportButtonProps, type IssueHistoryFilter, IssueReportMessageThread, type IssueReportingClient, type IssueReportingCopy, type IssueReportingCreateMode, IssueReportingPageConfig, type IssueReportingPageConfigProps, IssueReportingProvider, type IssueReportingProviderProps, ReportModeContext, type ReportModeContextValue, type ReportableInput, ReportableSection, type ReportableTargetDescriptor, defaultIssueReportingCopy, filterIssueReports, getEntryPointClassName, getEntryPointState, getIssueNoteLengthMessage, getIssueStatusBadgeLabel, getIssueStatusClassName, isClosedIssueStatus, isOpenIssueStatus, isReporterMessageConflict, issueReportingKeys, selectReporterVisibleMessages, useIssueReporting, useIssueReportingHistory, useIssueReportingMessageMutation, useIssueReportingMessages, useIssueReportingMutations, useIssueReportingStatus, useReportMode };
|