spaps-issue-reporting-react 0.1.0 → 0.1.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/README.md +137 -42
- package/dist/index.d.mts +28 -11
- package/dist/index.d.ts +28 -11
- package/dist/index.js +138 -39
- package/dist/index.mjs +141 -41
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,69 +1,164 @@
|
|
|
1
1
|
# spaps-issue-reporting-react
|
|
2
2
|
|
|
3
|
-
Shared React issue-reporting UI for SPAPS-
|
|
3
|
+
Shared React issue-reporting UI for SPAPS-compatible apps.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
- `package_name`: `spaps-issue-reporting-react`
|
|
7
|
-
- `latest_version`: `0.1.0`
|
|
8
|
-
- `minimum_runtime`: `Node.js >=18.0.0`
|
|
9
|
-
- `api_base_url`: `https://api.sweetpotato.dev`
|
|
5
|
+
Examples in this README use placeholder IDs and app labels. Replace them with values from your own product, session model, and API client.
|
|
10
6
|
|
|
11
|
-
##
|
|
7
|
+
## Install
|
|
12
8
|
|
|
13
9
|
```bash
|
|
14
|
-
npm install spaps-issue-reporting-react
|
|
10
|
+
npm install spaps-issue-reporting-react react react-dom @tanstack/react-query
|
|
15
11
|
```
|
|
16
12
|
|
|
17
|
-
|
|
13
|
+
This package targets `Node.js >=18` and React 18+.
|
|
18
14
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
## When It Fits
|
|
16
|
+
|
|
17
|
+
| Need | Package gives you |
|
|
18
|
+
| --- | --- |
|
|
19
|
+
| A visible issue-report entry point | Floating button with open/recent state |
|
|
20
|
+
| A guided reporting flow | Report mode, highlighted sections, create/edit/reply modal |
|
|
21
|
+
| A lightweight integration contract | Any client that exposes `issueReporting.*` methods |
|
|
22
|
+
| App-level control | Eligibility, reporter identity, scope, and copy stay in your app |
|
|
22
23
|
|
|
23
|
-
##
|
|
24
|
+
## Quick Start
|
|
24
25
|
|
|
25
26
|
```tsx
|
|
27
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
26
28
|
import {
|
|
27
29
|
FloatingIssueReportButton,
|
|
28
30
|
IssueReportingProvider,
|
|
29
|
-
|
|
31
|
+
ReportableSection,
|
|
30
32
|
} from "spaps-issue-reporting-react";
|
|
31
|
-
import {
|
|
33
|
+
import { spaps } from "./spapsClient";
|
|
32
34
|
|
|
33
|
-
|
|
35
|
+
const queryClient = new QueryClient();
|
|
36
|
+
|
|
37
|
+
export function AppShell() {
|
|
34
38
|
return (
|
|
35
|
-
<
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
<QueryClientProvider client={queryClient}>
|
|
40
|
+
<IssueReportingProvider
|
|
41
|
+
client={spaps}
|
|
42
|
+
isEligible={true}
|
|
43
|
+
principalId="user_123"
|
|
44
|
+
reporterRoleHint="staff"
|
|
45
|
+
allowTenantScope
|
|
46
|
+
defaultScope="mine"
|
|
47
|
+
>
|
|
48
|
+
<ReportableSection
|
|
49
|
+
reportableName={{
|
|
50
|
+
componentKey: "patient_chart",
|
|
51
|
+
componentLabel: "Patient Chart",
|
|
52
|
+
metadata: { area: "overview" },
|
|
53
|
+
}}
|
|
54
|
+
className="rounded-xl"
|
|
55
|
+
>
|
|
56
|
+
<PatientChart />
|
|
57
|
+
</ReportableSection>
|
|
58
|
+
|
|
59
|
+
<FloatingIssueReportButton />
|
|
60
|
+
</IssueReportingProvider>
|
|
61
|
+
</QueryClientProvider>
|
|
43
62
|
);
|
|
44
63
|
}
|
|
64
|
+
```
|
|
45
65
|
|
|
46
|
-
|
|
47
|
-
const reportMode = useReportMode();
|
|
66
|
+
## What Your App Still Owns
|
|
48
67
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
68
|
+
- A client with `issueReporting.getStatus`, `list`, `get`, `create`, `update`, and `reply`.
|
|
69
|
+
- Any auth and token refresh behavior needed by that client.
|
|
70
|
+
- Eligibility rules such as feature flags, account state, or role checks.
|
|
71
|
+
- The current principal ID and optional role hint passed into the provider.
|
|
72
|
+
- Whether users can switch between `mine` and `tenant` queue scope.
|
|
73
|
+
- Styling integration if your build strips package utility classes.
|
|
74
|
+
- Any app-specific copy overrides.
|
|
75
|
+
- Origin registration on the owning SPAPS application if the browser calls SPAPS directly with a publishable key.
|
|
76
|
+
|
|
77
|
+
For direct browser integrations, `allowed_origins` is stored on the SPAPS `applications` row that owns the publishable key. It is not configured on this package. If multiple hostnames share one SPAPS application, put all of them on that row. Updating `allowed_origins` does not require restarting SPAPS.
|
|
78
|
+
|
|
79
|
+
## Exported Surface
|
|
80
|
+
|
|
81
|
+
| Export | Purpose |
|
|
82
|
+
| --- | --- |
|
|
83
|
+
| `IssueReportingProvider` | Owns queries, modal state, copy, scope, and report-mode behavior |
|
|
84
|
+
| `FloatingIssueReportButton` | Renders the floating entry point, popover, and modal |
|
|
85
|
+
| `ReportableSection` | Marks a region as selectable when report mode is active |
|
|
86
|
+
| `useIssueReporting` | Full provider state, including `enterReportMode()` |
|
|
87
|
+
| `useIssueReportingStatus` | Query helper for summary state |
|
|
88
|
+
| `useIssueReportingHistory` | Query helper for filtered history lists |
|
|
89
|
+
| `useIssueReportingMutations` | Mutation helpers for create, update, and reply flows |
|
|
90
|
+
| `useReportMode` | Low-level selection state for custom wrappers |
|
|
91
|
+
|
|
92
|
+
## Styling Notes
|
|
93
|
+
|
|
94
|
+
- The package ships utility-class based markup for the button, popover, modal, and report-mode highlights.
|
|
95
|
+
- If your CSS build only scans local source files, include this package in the scan path or safelist the relevant classes.
|
|
96
|
+
- `FloatingIssueReportButton` accepts `className` and `positionClassName` for placement tweaks.
|
|
97
|
+
- Provider copy can be overridden with the `copy` prop.
|
|
98
|
+
|
|
99
|
+
## Development
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
cd packages/issue-reporting-react
|
|
103
|
+
npm ci
|
|
104
|
+
npm run build
|
|
105
|
+
npm test
|
|
58
106
|
```
|
|
59
107
|
|
|
60
|
-
##
|
|
108
|
+
## Troubleshooting
|
|
109
|
+
|
|
110
|
+
### The button renders but the modal never opens
|
|
111
|
+
|
|
112
|
+
Make sure `FloatingIssueReportButton` is rendered inside `IssueReportingProvider` and that `isEligible` is `true`.
|
|
113
|
+
|
|
114
|
+
### Report mode turns on, but sections do not respond
|
|
115
|
+
|
|
116
|
+
Wrap the target UI in `ReportableSection`, or call `useIssueReporting().selectPanel(...)` from a custom control.
|
|
117
|
+
|
|
118
|
+
### Tenant scope never appears
|
|
119
|
+
|
|
120
|
+
Set `allowTenantScope={true}`. The provider defaults to `mine`.
|
|
121
|
+
|
|
122
|
+
### Styles look unformatted
|
|
123
|
+
|
|
124
|
+
Include the package in your Tailwind or CSS-content scan, or override the rendered classes in your host app.
|
|
125
|
+
|
|
126
|
+
## Limitations
|
|
127
|
+
|
|
128
|
+
- This package assumes React Query is already part of the host app.
|
|
129
|
+
- It focuses on the issue-reporting UI flow; queue policy, triage rules, and backend delivery stay outside the package.
|
|
130
|
+
- Deep visual changes may require wrapping the exported components rather than using props alone.
|
|
131
|
+
|
|
132
|
+
## FAQ
|
|
133
|
+
|
|
134
|
+
### Do I need the full SPAPS SDK?
|
|
135
|
+
|
|
136
|
+
No. You only need a client object that implements the `issueReporting` methods expected by the provider.
|
|
137
|
+
|
|
138
|
+
### Can I launch report mode from my own button?
|
|
139
|
+
|
|
140
|
+
Yes. Use `useIssueReporting().enterReportMode()` inside the provider tree.
|
|
141
|
+
|
|
142
|
+
### Can I report plain strings instead of structured descriptors?
|
|
143
|
+
|
|
144
|
+
Yes. `reportableName` accepts either a string or a `{ componentKey, componentLabel, ... }` object.
|
|
145
|
+
|
|
146
|
+
### Can I hide the feature for some accounts?
|
|
147
|
+
|
|
148
|
+
Yes. Drive that from `isEligible`.
|
|
149
|
+
|
|
150
|
+
### Does this package manage tenant permissions?
|
|
151
|
+
|
|
152
|
+
No. It only renders scope choices that your app explicitly allows.
|
|
153
|
+
|
|
154
|
+
### Does this package manage CORS or `allowed_origins`?
|
|
155
|
+
|
|
156
|
+
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`.
|
|
157
|
+
|
|
158
|
+
## About Contributions
|
|
61
159
|
|
|
62
|
-
-
|
|
63
|
-
- Reportable target registration via `useReportMode`, `ReportableSection`, or equivalent wrappers.
|
|
64
|
-
- Tailwind content configuration so package classes are included in the host build.
|
|
65
|
-
- Any app-specific copy overrides passed through the provider.
|
|
160
|
+
> *About Contributions:* Please don't take this the wrong way, but I do not accept outside contributions for any of my projects. I simply don't have the mental bandwidth to review anything, and it's my name on the thing, so I'm responsible for any problems it causes; thus, the risk-reward is highly asymmetric from my perspective. I'd also have to worry about other "stakeholders," which seems unwise for tools I mostly make for myself for free. Feel free to submit issues, and even PRs if you want to illustrate a proposed fix, but know I won't merge them directly. Instead, I'll have Claude or Codex review submissions via `gh` and independently decide whether and how to address them. Bug reports in particular are welcome. Sorry if this offends, but I want to avoid wasted time and hurt feelings. I understand this isn't in sync with the prevailing open-source ethos that seeks community contributions, but it's the only way I can move at this velocity and keep my sanity.
|
|
66
161
|
|
|
67
|
-
##
|
|
162
|
+
## License
|
|
68
163
|
|
|
69
|
-
|
|
164
|
+
`UNLICENSED`
|
package/dist/index.d.mts
CHANGED
|
@@ -2,18 +2,21 @@ 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 { IssueReportStatusResult, IssueReportStatus, IssueReportListResult, IssueReport, CreateIssueReportRequest, UpdateIssueReportRequest, ReplyIssueReportRequest } from 'spaps-types';
|
|
6
|
-
export { CreateIssueReportRequest, IssueReport, IssueReportListResult, IssueReportStatus, IssueReportStatusResult, ReplyIssueReportRequest, UpdateIssueReportRequest } from 'spaps-types';
|
|
5
|
+
import { IssueReportScope, IssueReportStatusResult, IssueReportStatus, IssueReportListResult, IssueReport, CreateIssueReportRequest, UpdateIssueReportRequest, ReplyIssueReportRequest } from 'spaps-types';
|
|
6
|
+
export { CreateIssueReportRequest, IssueReport, IssueReportListResult, IssueReportScope, IssueReportStatus, IssueReportStatusResult, 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
|
|
|
10
10
|
interface IssueReportingClient {
|
|
11
11
|
issueReporting: {
|
|
12
|
-
getStatus: (
|
|
12
|
+
getStatus: (params?: {
|
|
13
|
+
scope?: IssueReportScope;
|
|
14
|
+
}) => Promise<IssueReportStatusResult>;
|
|
13
15
|
list: (params?: {
|
|
14
16
|
status?: IssueReportStatus;
|
|
15
17
|
limit?: number;
|
|
16
18
|
offset?: number;
|
|
19
|
+
scope?: IssueReportScope;
|
|
17
20
|
}) => Promise<IssueReportListResult>;
|
|
18
21
|
get: (issueReportId: string) => Promise<IssueReport>;
|
|
19
22
|
create: (payload: CreateIssueReportRequest) => Promise<IssueReport>;
|
|
@@ -28,20 +31,24 @@ interface ReportableTargetDescriptor {
|
|
|
28
31
|
metadata?: Record<string, unknown>;
|
|
29
32
|
}
|
|
30
33
|
type ReportableInput = string | ReportableTargetDescriptor;
|
|
31
|
-
type IssueHistoryFilter = "all" | "
|
|
34
|
+
type IssueHistoryFilter = "all" | "unresolved" | "resolved";
|
|
32
35
|
interface IssueReportingCopy {
|
|
33
36
|
entryAriaLabel: string;
|
|
34
37
|
popoverTitle: string;
|
|
35
38
|
reportNewAction: string;
|
|
36
39
|
filtersAll: string;
|
|
37
|
-
|
|
38
|
-
|
|
40
|
+
filtersUnresolved: string;
|
|
41
|
+
filtersResolved: string;
|
|
39
42
|
historyHelpText: string;
|
|
40
43
|
historyLoading: string;
|
|
41
44
|
historyLoadFailed: string;
|
|
45
|
+
statusLoadFailed: string;
|
|
42
46
|
emptyAll: string;
|
|
43
|
-
|
|
44
|
-
|
|
47
|
+
emptyUnresolved: string;
|
|
48
|
+
emptyResolved: string;
|
|
49
|
+
scopeLabel: string;
|
|
50
|
+
scopeMine: string;
|
|
51
|
+
scopeTenant: string;
|
|
45
52
|
reportModeTitle: string;
|
|
46
53
|
reportModeDescription: string;
|
|
47
54
|
reportModeCancelAction: string;
|
|
@@ -63,12 +70,18 @@ interface IssueReportingCopy {
|
|
|
63
70
|
hydrateLoading: string;
|
|
64
71
|
hydrateFailed: string;
|
|
65
72
|
retryAction: string;
|
|
73
|
+
originHumanLabel: string;
|
|
74
|
+
originMachineLabel: string;
|
|
75
|
+
machineOriginFallback: string;
|
|
66
76
|
}
|
|
67
77
|
interface IssueReportingProviderProps {
|
|
68
78
|
client: IssueReportingClient;
|
|
69
79
|
isEligible: boolean;
|
|
70
80
|
reporterRoleHint?: string;
|
|
81
|
+
principalId?: string | null;
|
|
71
82
|
getPageUrl?: () => string;
|
|
83
|
+
defaultScope?: IssueReportScope;
|
|
84
|
+
allowTenantScope?: boolean;
|
|
72
85
|
copy?: Partial<IssueReportingCopy>;
|
|
73
86
|
children: ReactNode;
|
|
74
87
|
}
|
|
@@ -106,6 +119,7 @@ type IssueReportingContextValue = {
|
|
|
106
119
|
client: IssueReportingProviderProps["client"];
|
|
107
120
|
isEligible: boolean;
|
|
108
121
|
reporterRoleHint?: string;
|
|
122
|
+
principalId: string | null;
|
|
109
123
|
copy: IssueReportingCopy;
|
|
110
124
|
isReportMode: boolean;
|
|
111
125
|
enterReportMode: () => void;
|
|
@@ -118,12 +132,15 @@ type IssueReportingContextValue = {
|
|
|
118
132
|
closeModal: () => void;
|
|
119
133
|
openExistingIssueModal: (issueReportId: string, mode: Exclude<ModalMode, "create">) => Promise<void>;
|
|
120
134
|
retryModalHydration: () => Promise<void>;
|
|
135
|
+
scope: IssueReportScope;
|
|
136
|
+
setScope: (scope: IssueReportScope) => void;
|
|
137
|
+
allowTenantScope: boolean;
|
|
121
138
|
};
|
|
122
139
|
declare const defaultIssueReportingCopy: IssueReportingCopy;
|
|
123
140
|
declare const issueReportingKeys: {
|
|
124
141
|
all: readonly ["spaps-issue-reporting"];
|
|
125
|
-
status: () => readonly ["spaps-issue-reporting", "status"];
|
|
126
|
-
history: () => readonly ["spaps-issue-reporting", "history"];
|
|
142
|
+
status: (scope: IssueReportScope) => readonly ["spaps-issue-reporting", "status", IssueReportScope];
|
|
143
|
+
history: (scope: IssueReportScope) => readonly ["spaps-issue-reporting", "history", IssueReportScope];
|
|
127
144
|
detail: (issueReportId: string) => readonly ["spaps-issue-reporting", "detail", string];
|
|
128
145
|
};
|
|
129
146
|
declare function isOpenIssueStatus(status: IssueReportStatus): boolean;
|
|
@@ -330,7 +347,7 @@ declare function useIssueReportingMutations(): {
|
|
|
330
347
|
reporterRoleHint?: string;
|
|
331
348
|
}, unknown>;
|
|
332
349
|
};
|
|
333
|
-
declare function IssueReportingProvider({ client, isEligible, reporterRoleHint, getPageUrl, copy, children, }: IssueReportingProviderProps): react_jsx_runtime.JSX.Element;
|
|
350
|
+
declare function IssueReportingProvider({ client, isEligible, reporterRoleHint, principalId, getPageUrl, defaultScope, allowTenantScope, copy, children, }: IssueReportingProviderProps): react_jsx_runtime.JSX.Element;
|
|
334
351
|
|
|
335
352
|
interface ReportModeContextValue {
|
|
336
353
|
isReportMode: boolean;
|
package/dist/index.d.ts
CHANGED
|
@@ -2,18 +2,21 @@ 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 { IssueReportStatusResult, IssueReportStatus, IssueReportListResult, IssueReport, CreateIssueReportRequest, UpdateIssueReportRequest, ReplyIssueReportRequest } from 'spaps-types';
|
|
6
|
-
export { CreateIssueReportRequest, IssueReport, IssueReportListResult, IssueReportStatus, IssueReportStatusResult, ReplyIssueReportRequest, UpdateIssueReportRequest } from 'spaps-types';
|
|
5
|
+
import { IssueReportScope, IssueReportStatusResult, IssueReportStatus, IssueReportListResult, IssueReport, CreateIssueReportRequest, UpdateIssueReportRequest, ReplyIssueReportRequest } from 'spaps-types';
|
|
6
|
+
export { CreateIssueReportRequest, IssueReport, IssueReportListResult, IssueReportScope, IssueReportStatus, IssueReportStatusResult, 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
|
|
|
10
10
|
interface IssueReportingClient {
|
|
11
11
|
issueReporting: {
|
|
12
|
-
getStatus: (
|
|
12
|
+
getStatus: (params?: {
|
|
13
|
+
scope?: IssueReportScope;
|
|
14
|
+
}) => Promise<IssueReportStatusResult>;
|
|
13
15
|
list: (params?: {
|
|
14
16
|
status?: IssueReportStatus;
|
|
15
17
|
limit?: number;
|
|
16
18
|
offset?: number;
|
|
19
|
+
scope?: IssueReportScope;
|
|
17
20
|
}) => Promise<IssueReportListResult>;
|
|
18
21
|
get: (issueReportId: string) => Promise<IssueReport>;
|
|
19
22
|
create: (payload: CreateIssueReportRequest) => Promise<IssueReport>;
|
|
@@ -28,20 +31,24 @@ interface ReportableTargetDescriptor {
|
|
|
28
31
|
metadata?: Record<string, unknown>;
|
|
29
32
|
}
|
|
30
33
|
type ReportableInput = string | ReportableTargetDescriptor;
|
|
31
|
-
type IssueHistoryFilter = "all" | "
|
|
34
|
+
type IssueHistoryFilter = "all" | "unresolved" | "resolved";
|
|
32
35
|
interface IssueReportingCopy {
|
|
33
36
|
entryAriaLabel: string;
|
|
34
37
|
popoverTitle: string;
|
|
35
38
|
reportNewAction: string;
|
|
36
39
|
filtersAll: string;
|
|
37
|
-
|
|
38
|
-
|
|
40
|
+
filtersUnresolved: string;
|
|
41
|
+
filtersResolved: string;
|
|
39
42
|
historyHelpText: string;
|
|
40
43
|
historyLoading: string;
|
|
41
44
|
historyLoadFailed: string;
|
|
45
|
+
statusLoadFailed: string;
|
|
42
46
|
emptyAll: string;
|
|
43
|
-
|
|
44
|
-
|
|
47
|
+
emptyUnresolved: string;
|
|
48
|
+
emptyResolved: string;
|
|
49
|
+
scopeLabel: string;
|
|
50
|
+
scopeMine: string;
|
|
51
|
+
scopeTenant: string;
|
|
45
52
|
reportModeTitle: string;
|
|
46
53
|
reportModeDescription: string;
|
|
47
54
|
reportModeCancelAction: string;
|
|
@@ -63,12 +70,18 @@ interface IssueReportingCopy {
|
|
|
63
70
|
hydrateLoading: string;
|
|
64
71
|
hydrateFailed: string;
|
|
65
72
|
retryAction: string;
|
|
73
|
+
originHumanLabel: string;
|
|
74
|
+
originMachineLabel: string;
|
|
75
|
+
machineOriginFallback: string;
|
|
66
76
|
}
|
|
67
77
|
interface IssueReportingProviderProps {
|
|
68
78
|
client: IssueReportingClient;
|
|
69
79
|
isEligible: boolean;
|
|
70
80
|
reporterRoleHint?: string;
|
|
81
|
+
principalId?: string | null;
|
|
71
82
|
getPageUrl?: () => string;
|
|
83
|
+
defaultScope?: IssueReportScope;
|
|
84
|
+
allowTenantScope?: boolean;
|
|
72
85
|
copy?: Partial<IssueReportingCopy>;
|
|
73
86
|
children: ReactNode;
|
|
74
87
|
}
|
|
@@ -106,6 +119,7 @@ type IssueReportingContextValue = {
|
|
|
106
119
|
client: IssueReportingProviderProps["client"];
|
|
107
120
|
isEligible: boolean;
|
|
108
121
|
reporterRoleHint?: string;
|
|
122
|
+
principalId: string | null;
|
|
109
123
|
copy: IssueReportingCopy;
|
|
110
124
|
isReportMode: boolean;
|
|
111
125
|
enterReportMode: () => void;
|
|
@@ -118,12 +132,15 @@ type IssueReportingContextValue = {
|
|
|
118
132
|
closeModal: () => void;
|
|
119
133
|
openExistingIssueModal: (issueReportId: string, mode: Exclude<ModalMode, "create">) => Promise<void>;
|
|
120
134
|
retryModalHydration: () => Promise<void>;
|
|
135
|
+
scope: IssueReportScope;
|
|
136
|
+
setScope: (scope: IssueReportScope) => void;
|
|
137
|
+
allowTenantScope: boolean;
|
|
121
138
|
};
|
|
122
139
|
declare const defaultIssueReportingCopy: IssueReportingCopy;
|
|
123
140
|
declare const issueReportingKeys: {
|
|
124
141
|
all: readonly ["spaps-issue-reporting"];
|
|
125
|
-
status: () => readonly ["spaps-issue-reporting", "status"];
|
|
126
|
-
history: () => readonly ["spaps-issue-reporting", "history"];
|
|
142
|
+
status: (scope: IssueReportScope) => readonly ["spaps-issue-reporting", "status", IssueReportScope];
|
|
143
|
+
history: (scope: IssueReportScope) => readonly ["spaps-issue-reporting", "history", IssueReportScope];
|
|
127
144
|
detail: (issueReportId: string) => readonly ["spaps-issue-reporting", "detail", string];
|
|
128
145
|
};
|
|
129
146
|
declare function isOpenIssueStatus(status: IssueReportStatus): boolean;
|
|
@@ -330,7 +347,7 @@ declare function useIssueReportingMutations(): {
|
|
|
330
347
|
reporterRoleHint?: string;
|
|
331
348
|
}, unknown>;
|
|
332
349
|
};
|
|
333
|
-
declare function IssueReportingProvider({ client, isEligible, reporterRoleHint, getPageUrl, copy, children, }: IssueReportingProviderProps): react_jsx_runtime.JSX.Element;
|
|
350
|
+
declare function IssueReportingProvider({ client, isEligible, reporterRoleHint, principalId, getPageUrl, defaultScope, allowTenantScope, copy, children, }: IssueReportingProviderProps): react_jsx_runtime.JSX.Element;
|
|
334
351
|
|
|
335
352
|
interface ReportModeContextValue {
|
|
336
353
|
isReportMode: boolean;
|
package/dist/index.js
CHANGED
|
@@ -94,14 +94,18 @@ var defaultIssueReportingCopy = {
|
|
|
94
94
|
popoverTitle: "Issue Reports",
|
|
95
95
|
reportNewAction: "Report New Issue",
|
|
96
96
|
filtersAll: "All",
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
filtersUnresolved: "Unresolved",
|
|
98
|
+
filtersResolved: "Resolved",
|
|
99
99
|
historyHelpText: "We read every report. If something is broken or could work better, report it here and it goes straight to the people building the app.",
|
|
100
100
|
historyLoading: "Loading issues...",
|
|
101
101
|
historyLoadFailed: "Failed to load issues",
|
|
102
|
+
statusLoadFailed: "Failed to load issue status",
|
|
102
103
|
emptyAll: "No issues reported yet",
|
|
103
|
-
|
|
104
|
-
|
|
104
|
+
emptyUnresolved: "No unresolved issues",
|
|
105
|
+
emptyResolved: "No resolved or ignored issues",
|
|
106
|
+
scopeLabel: "Scope",
|
|
107
|
+
scopeMine: "Mine",
|
|
108
|
+
scopeTenant: "Tenant",
|
|
105
109
|
reportModeTitle: "Report mode is active",
|
|
106
110
|
reportModeDescription: "Click a highlighted section to capture the broken surface.",
|
|
107
111
|
reportModeCancelAction: "Cancel",
|
|
@@ -122,12 +126,15 @@ var defaultIssueReportingCopy = {
|
|
|
122
126
|
replyAction: "Reply",
|
|
123
127
|
hydrateLoading: "Loading the latest issue details...",
|
|
124
128
|
hydrateFailed: "Failed to load the latest issue details.",
|
|
125
|
-
retryAction: "Retry"
|
|
129
|
+
retryAction: "Retry",
|
|
130
|
+
originHumanLabel: "Human",
|
|
131
|
+
originMachineLabel: "Machine",
|
|
132
|
+
machineOriginFallback: "system"
|
|
126
133
|
};
|
|
127
134
|
var issueReportingKeys = {
|
|
128
135
|
all: ["spaps-issue-reporting"],
|
|
129
|
-
status: () => [...issueReportingKeys.all, "status"],
|
|
130
|
-
history: () => [...issueReportingKeys.all, "history"],
|
|
136
|
+
status: (scope) => [...issueReportingKeys.all, "status", scope],
|
|
137
|
+
history: (scope) => [...issueReportingKeys.all, "history", scope],
|
|
131
138
|
detail: (issueReportId) => [...issueReportingKeys.all, "detail", issueReportId]
|
|
132
139
|
};
|
|
133
140
|
function resolvePageUrl(getPageUrl) {
|
|
@@ -139,6 +146,12 @@ function resolvePageUrl(getPageUrl) {
|
|
|
139
146
|
}
|
|
140
147
|
return `${window.location.pathname}${window.location.search}${window.location.hash}`;
|
|
141
148
|
}
|
|
149
|
+
function resolveInitialScope(defaultScope, allowTenantScope) {
|
|
150
|
+
if (allowTenantScope && defaultScope === "tenant") {
|
|
151
|
+
return "tenant";
|
|
152
|
+
}
|
|
153
|
+
return "mine";
|
|
154
|
+
}
|
|
142
155
|
function normalizeTarget(target, getPageUrl) {
|
|
143
156
|
if (typeof target === "string") {
|
|
144
157
|
const normalized = target.trim();
|
|
@@ -182,10 +195,10 @@ function isClosedIssueStatus(status) {
|
|
|
182
195
|
return status === "resolved" || status === "ignored";
|
|
183
196
|
}
|
|
184
197
|
function filterIssueReports(issues, filter) {
|
|
185
|
-
if (filter === "
|
|
198
|
+
if (filter === "unresolved") {
|
|
186
199
|
return issues.filter((issue) => isOpenIssueStatus(issue.status));
|
|
187
200
|
}
|
|
188
|
-
if (filter === "
|
|
201
|
+
if (filter === "resolved") {
|
|
189
202
|
return issues.filter((issue) => isClosedIssueStatus(issue.status));
|
|
190
203
|
}
|
|
191
204
|
return issues;
|
|
@@ -251,20 +264,20 @@ function useIssueReporting() {
|
|
|
251
264
|
return context;
|
|
252
265
|
}
|
|
253
266
|
function useIssueReportingStatus() {
|
|
254
|
-
const { client, isEligible } = useIssueReporting();
|
|
267
|
+
const { client, isEligible, scope } = useIssueReporting();
|
|
255
268
|
return (0, import_react_query.useQuery)({
|
|
256
|
-
queryKey: issueReportingKeys.status(),
|
|
257
|
-
queryFn: () => client.issueReporting.getStatus(),
|
|
269
|
+
queryKey: issueReportingKeys.status(scope),
|
|
270
|
+
queryFn: () => client.issueReporting.getStatus({ scope }),
|
|
258
271
|
enabled: isEligible,
|
|
259
272
|
retry: false,
|
|
260
273
|
staleTime: 3e4
|
|
261
274
|
});
|
|
262
275
|
}
|
|
263
276
|
function useIssueReportingHistory(filter = "all") {
|
|
264
|
-
const { client, isEligible } = useIssueReporting();
|
|
277
|
+
const { client, isEligible, scope } = useIssueReporting();
|
|
265
278
|
const query = (0, import_react_query.useQuery)({
|
|
266
|
-
queryKey: issueReportingKeys.history(),
|
|
267
|
-
queryFn: () => client.issueReporting.list({ limit: LIST_LIMIT, offset: 0 }),
|
|
279
|
+
queryKey: issueReportingKeys.history(scope),
|
|
280
|
+
queryFn: () => client.issueReporting.list({ limit: LIST_LIMIT, offset: 0, scope }),
|
|
268
281
|
enabled: isEligible,
|
|
269
282
|
retry: false
|
|
270
283
|
});
|
|
@@ -275,7 +288,7 @@ function useIssueReportingHistory(filter = "all") {
|
|
|
275
288
|
return {
|
|
276
289
|
...query,
|
|
277
290
|
items,
|
|
278
|
-
total: items.length
|
|
291
|
+
total: query.data?.total ?? items.length
|
|
279
292
|
};
|
|
280
293
|
}
|
|
281
294
|
function useIssueReportingMutations() {
|
|
@@ -318,7 +331,10 @@ function IssueReportingProvider({
|
|
|
318
331
|
client,
|
|
319
332
|
isEligible,
|
|
320
333
|
reporterRoleHint,
|
|
334
|
+
principalId,
|
|
321
335
|
getPageUrl,
|
|
336
|
+
defaultScope,
|
|
337
|
+
allowTenantScope = false,
|
|
322
338
|
copy,
|
|
323
339
|
children
|
|
324
340
|
}) {
|
|
@@ -333,6 +349,12 @@ function IssueReportingProvider({
|
|
|
333
349
|
const [isReportMode, setIsReportMode] = (0, import_react2.useState)(false);
|
|
334
350
|
const [isPopoverOpen, setIsPopoverOpen] = (0, import_react2.useState)(false);
|
|
335
351
|
const [modalState, setModalState] = (0, import_react2.useState)(initialModalState);
|
|
352
|
+
const [scope, setScope] = (0, import_react2.useState)(
|
|
353
|
+
() => resolveInitialScope(defaultScope, allowTenantScope)
|
|
354
|
+
);
|
|
355
|
+
(0, import_react2.useEffect)(() => {
|
|
356
|
+
setScope(resolveInitialScope(defaultScope, allowTenantScope));
|
|
357
|
+
}, [allowTenantScope, defaultScope]);
|
|
336
358
|
const closePopover = (0, import_react2.useCallback)(() => {
|
|
337
359
|
setIsPopoverOpen(false);
|
|
338
360
|
}, []);
|
|
@@ -426,6 +448,7 @@ function IssueReportingProvider({
|
|
|
426
448
|
client,
|
|
427
449
|
isEligible,
|
|
428
450
|
reporterRoleHint,
|
|
451
|
+
principalId: principalId ?? null,
|
|
429
452
|
copy: mergedCopy,
|
|
430
453
|
isReportMode,
|
|
431
454
|
enterReportMode,
|
|
@@ -437,9 +460,13 @@ function IssueReportingProvider({
|
|
|
437
460
|
modalState,
|
|
438
461
|
closeModal,
|
|
439
462
|
openExistingIssueModal,
|
|
440
|
-
retryModalHydration
|
|
463
|
+
retryModalHydration,
|
|
464
|
+
scope,
|
|
465
|
+
setScope,
|
|
466
|
+
allowTenantScope
|
|
441
467
|
}),
|
|
442
468
|
[
|
|
469
|
+
allowTenantScope,
|
|
443
470
|
cancelReportMode,
|
|
444
471
|
client,
|
|
445
472
|
closeModal,
|
|
@@ -452,8 +479,10 @@ function IssueReportingProvider({
|
|
|
452
479
|
modalState,
|
|
453
480
|
openExistingIssueModal,
|
|
454
481
|
openPopover,
|
|
482
|
+
principalId,
|
|
455
483
|
reporterRoleHint,
|
|
456
484
|
retryModalHydration,
|
|
485
|
+
scope,
|
|
457
486
|
selectPanel
|
|
458
487
|
]
|
|
459
488
|
);
|
|
@@ -484,6 +513,47 @@ function formatRelativeTime(timestamp) {
|
|
|
484
513
|
addSuffix: true
|
|
485
514
|
});
|
|
486
515
|
}
|
|
516
|
+
function resolveErrorMessage(error, fallback) {
|
|
517
|
+
if (error instanceof Error && error.message.trim()) {
|
|
518
|
+
return error.message;
|
|
519
|
+
}
|
|
520
|
+
if (error && typeof error === "object" && "message" in error && typeof error.message === "string" && error.message.trim()) {
|
|
521
|
+
return error.message;
|
|
522
|
+
}
|
|
523
|
+
return fallback;
|
|
524
|
+
}
|
|
525
|
+
function resolveReporterId(issue) {
|
|
526
|
+
return issue.reporter_principal_id ?? issue.reporter_user_id ?? null;
|
|
527
|
+
}
|
|
528
|
+
function resolveReporterType(issue) {
|
|
529
|
+
if (issue.reporter_principal_type) {
|
|
530
|
+
return issue.reporter_principal_type;
|
|
531
|
+
}
|
|
532
|
+
if (issue.machine_principal_id || issue.agent_id) {
|
|
533
|
+
return "machine";
|
|
534
|
+
}
|
|
535
|
+
return "human";
|
|
536
|
+
}
|
|
537
|
+
function getIssueAction(issue, principalId) {
|
|
538
|
+
if (!principalId) {
|
|
539
|
+
return null;
|
|
540
|
+
}
|
|
541
|
+
if (resolveReporterType(issue) !== "human") {
|
|
542
|
+
return null;
|
|
543
|
+
}
|
|
544
|
+
if (resolveReporterId(issue) !== principalId) {
|
|
545
|
+
return null;
|
|
546
|
+
}
|
|
547
|
+
return isClosedIssueStatus(issue.status) ? "reply" : "edit";
|
|
548
|
+
}
|
|
549
|
+
function getIssueOriginText(issue, copy) {
|
|
550
|
+
if (resolveReporterType(issue) === "machine") {
|
|
551
|
+
const provenance = issue.agent_id ?? issue.machine_principal_id ?? issue.reporter_display_name ?? copy.machineOriginFallback;
|
|
552
|
+
return `${copy.originMachineLabel} \xB7 ${provenance}`;
|
|
553
|
+
}
|
|
554
|
+
const humanName = issue.reporter_display_name?.trim();
|
|
555
|
+
return humanName ? `${copy.originHumanLabel} \xB7 ${humanName}` : copy.originHumanLabel;
|
|
556
|
+
}
|
|
487
557
|
function IssueReportModeBanner() {
|
|
488
558
|
const { copy } = useIssueReporting();
|
|
489
559
|
const reportMode = useReportMode();
|
|
@@ -509,7 +579,7 @@ function IssueList({
|
|
|
509
579
|
onEdit,
|
|
510
580
|
onReply
|
|
511
581
|
}) {
|
|
512
|
-
const { copy } = useIssueReporting();
|
|
582
|
+
const { copy, principalId } = useIssueReporting();
|
|
513
583
|
const history = useIssueReportingHistory(filter);
|
|
514
584
|
if (history.isPending) {
|
|
515
585
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2 rounded-2xl border border-slate-200 bg-slate-50 px-4 py-5 text-sm text-slate-600", children: [
|
|
@@ -532,19 +602,18 @@ function IssueList({
|
|
|
532
602
|
] });
|
|
533
603
|
}
|
|
534
604
|
if (history.items.length === 0) {
|
|
535
|
-
const emptyMessage = filter === "
|
|
605
|
+
const emptyMessage = filter === "unresolved" ? copy.emptyUnresolved : filter === "resolved" ? copy.emptyResolved : copy.emptyAll;
|
|
536
606
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "rounded-2xl border border-dashed border-slate-200 bg-slate-50 px-4 py-8 text-center text-sm text-slate-500", children: emptyMessage });
|
|
537
607
|
}
|
|
538
608
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "max-h-80 space-y-2 overflow-y-auto pr-1", children: history.items.map((issue) => {
|
|
539
|
-
const
|
|
540
|
-
const
|
|
541
|
-
const onAction = () => isClosed ? onReply(issue.id) : onEdit(issue.id);
|
|
609
|
+
const action = getIssueAction(issue, principalId);
|
|
610
|
+
const onAction = action === "edit" ? () => onEdit(issue.id) : action === "reply" ? () => onReply(issue.id) : null;
|
|
542
611
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
543
612
|
"div",
|
|
544
613
|
{
|
|
545
614
|
className: "rounded-2xl border border-slate-200 bg-white px-3 py-3 shadow-sm",
|
|
546
615
|
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-start gap-3", children: [
|
|
547
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "mt-0.5 flex-shrink-0", children:
|
|
616
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "mt-0.5 flex-shrink-0", children: isClosedIssueStatus(issue.status) ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react3.CheckCircle, { className: "h-4 w-4 text-emerald-600", weight: "fill" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react3.Circle, { className: "h-4 w-4 text-rose-600", weight: "fill" }) }),
|
|
548
617
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "min-w-0 flex-1", children: [
|
|
549
618
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-start justify-between gap-2", children: [
|
|
550
619
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "min-w-0", children: [
|
|
@@ -560,18 +629,19 @@ function IssueList({
|
|
|
560
629
|
children: getIssueStatusBadgeLabel(issue.status)
|
|
561
630
|
}
|
|
562
631
|
),
|
|
632
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "rounded-full bg-slate-100 px-2 py-0.5 text-[11px] font-medium text-slate-600", children: getIssueOriginText(issue, copy) }),
|
|
563
633
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-xs text-slate-400", children: formatRelativeTime(issue.updated_at) })
|
|
564
634
|
] })
|
|
565
635
|
] }),
|
|
566
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
636
|
+
action && onAction ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
567
637
|
"button",
|
|
568
638
|
{
|
|
569
639
|
type: "button",
|
|
570
640
|
className: "rounded-full border border-slate-200 px-3 py-1 text-xs font-medium text-slate-700 transition hover:bg-slate-50",
|
|
571
641
|
onClick: onAction,
|
|
572
|
-
children:
|
|
642
|
+
children: action === "reply" ? copy.replyAction : copy.editAction
|
|
573
643
|
}
|
|
574
|
-
)
|
|
644
|
+
) : null
|
|
575
645
|
] }),
|
|
576
646
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "mt-2 text-sm text-slate-600", children: truncate(issue.note) }),
|
|
577
647
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "mt-2 truncate text-xs text-slate-400", children: issue.target.page_url })
|
|
@@ -590,7 +660,10 @@ function IssueReportPopover({ children }) {
|
|
|
590
660
|
openPopover,
|
|
591
661
|
closePopover,
|
|
592
662
|
enterReportMode,
|
|
593
|
-
openExistingIssueModal
|
|
663
|
+
openExistingIssueModal,
|
|
664
|
+
scope,
|
|
665
|
+
setScope,
|
|
666
|
+
allowTenantScope
|
|
594
667
|
} = useIssueReporting();
|
|
595
668
|
const history = useIssueReportingHistory("all");
|
|
596
669
|
const status = useIssueReportingStatus();
|
|
@@ -598,11 +671,12 @@ function IssueReportPopover({ children }) {
|
|
|
598
671
|
const counts = (0, import_react4.useMemo)(
|
|
599
672
|
() => ({
|
|
600
673
|
all: allItems.length,
|
|
601
|
-
|
|
602
|
-
|
|
674
|
+
unresolved: filterIssueReports(allItems, "unresolved").length,
|
|
675
|
+
resolved: filterIssueReports(allItems, "resolved").length
|
|
603
676
|
}),
|
|
604
677
|
[allItems]
|
|
605
678
|
);
|
|
679
|
+
const statusSummary = status.data ? `${status.data.open_count} open \xB7 ${status.data.recent_resolved_count} recently resolved` : "Status reflects the active scope.";
|
|
606
680
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
607
681
|
Popover.Root,
|
|
608
682
|
{
|
|
@@ -621,11 +695,7 @@ function IssueReportPopover({ children }) {
|
|
|
621
695
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center justify-between gap-3", children: [
|
|
622
696
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
623
697
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { className: "text-sm font-semibold text-slate-900", children: copy.popoverTitle }),
|
|
624
|
-
|
|
625
|
-
status.data.open_count,
|
|
626
|
-
" open issue",
|
|
627
|
-
status.data.open_count === 1 ? "" : "s"
|
|
628
|
-
] }) : null
|
|
698
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "mt-1 text-xs text-slate-500", children: statusSummary })
|
|
629
699
|
] }),
|
|
630
700
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
631
701
|
"button",
|
|
@@ -640,10 +710,41 @@ function IssueReportPopover({ children }) {
|
|
|
640
710
|
}
|
|
641
711
|
)
|
|
642
712
|
] }),
|
|
713
|
+
status.error ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "space-y-3 rounded-2xl border border-rose-200 bg-rose-50 px-4 py-4 text-sm text-rose-700", children: [
|
|
714
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { children: status.error.message || copy.statusLoadFailed }),
|
|
715
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
716
|
+
"button",
|
|
717
|
+
{
|
|
718
|
+
type: "button",
|
|
719
|
+
className: "rounded-full border border-rose-300 px-3 py-1 font-medium transition hover:bg-rose-100",
|
|
720
|
+
onClick: () => status.refetch(),
|
|
721
|
+
children: copy.retryAction
|
|
722
|
+
}
|
|
723
|
+
)
|
|
724
|
+
] }) : null,
|
|
725
|
+
allowTenantScope ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "space-y-2", children: [
|
|
726
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "text-[11px] font-medium uppercase tracking-wide text-slate-500", children: copy.scopeLabel }),
|
|
727
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex gap-2", children: [
|
|
728
|
+
["tenant", copy.scopeTenant],
|
|
729
|
+
["mine", copy.scopeMine]
|
|
730
|
+
].map(([value, label]) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
731
|
+
"button",
|
|
732
|
+
{
|
|
733
|
+
type: "button",
|
|
734
|
+
className: cn(
|
|
735
|
+
"rounded-full px-3 py-1.5 text-xs font-medium transition",
|
|
736
|
+
scope === value ? "bg-slate-900 text-white" : "bg-slate-100 text-slate-600 hover:bg-slate-200"
|
|
737
|
+
),
|
|
738
|
+
onClick: () => setScope(value),
|
|
739
|
+
children: label
|
|
740
|
+
},
|
|
741
|
+
value
|
|
742
|
+
)) })
|
|
743
|
+
] }) : null,
|
|
643
744
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex gap-2", children: [
|
|
644
745
|
["all", copy.filtersAll, counts.all],
|
|
645
|
-
["
|
|
646
|
-
["
|
|
746
|
+
["unresolved", copy.filtersUnresolved, counts.unresolved],
|
|
747
|
+
["resolved", copy.filtersResolved, counts.resolved]
|
|
647
748
|
].map(([value, label, count]) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
648
749
|
"button",
|
|
649
750
|
{
|
|
@@ -731,9 +832,7 @@ function IssueReportModal() {
|
|
|
731
832
|
}
|
|
732
833
|
closeModal();
|
|
733
834
|
} catch (submissionError) {
|
|
734
|
-
setSubmitError(
|
|
735
|
-
submissionError instanceof Error ? submissionError.message : "Failed to submit issue report"
|
|
736
|
-
);
|
|
835
|
+
setSubmitError(resolveErrorMessage(submissionError, "Failed to submit issue report"));
|
|
737
836
|
}
|
|
738
837
|
};
|
|
739
838
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Dialog.Root, { open: isOpen, onOpenChange: (open) => !open && closeModal(), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Dialog.Portal, { children: [
|
package/dist/index.mjs
CHANGED
|
@@ -9,13 +9,14 @@ import {
|
|
|
9
9
|
X
|
|
10
10
|
} from "@phosphor-icons/react";
|
|
11
11
|
import { formatDistanceToNow } from "date-fns";
|
|
12
|
-
import { useEffect, useMemo as useMemo2, useState as useState2 } from "react";
|
|
12
|
+
import { useEffect as useEffect2, useMemo as useMemo2, useState as useState2 } from "react";
|
|
13
13
|
|
|
14
14
|
// src/provider.tsx
|
|
15
15
|
import {
|
|
16
16
|
createContext as createContext2,
|
|
17
17
|
useCallback,
|
|
18
18
|
useContext as useContext2,
|
|
19
|
+
useEffect,
|
|
19
20
|
useMemo,
|
|
20
21
|
useState
|
|
21
22
|
} from "react";
|
|
@@ -56,14 +57,18 @@ var defaultIssueReportingCopy = {
|
|
|
56
57
|
popoverTitle: "Issue Reports",
|
|
57
58
|
reportNewAction: "Report New Issue",
|
|
58
59
|
filtersAll: "All",
|
|
59
|
-
|
|
60
|
-
|
|
60
|
+
filtersUnresolved: "Unresolved",
|
|
61
|
+
filtersResolved: "Resolved",
|
|
61
62
|
historyHelpText: "We read every report. If something is broken or could work better, report it here and it goes straight to the people building the app.",
|
|
62
63
|
historyLoading: "Loading issues...",
|
|
63
64
|
historyLoadFailed: "Failed to load issues",
|
|
65
|
+
statusLoadFailed: "Failed to load issue status",
|
|
64
66
|
emptyAll: "No issues reported yet",
|
|
65
|
-
|
|
66
|
-
|
|
67
|
+
emptyUnresolved: "No unresolved issues",
|
|
68
|
+
emptyResolved: "No resolved or ignored issues",
|
|
69
|
+
scopeLabel: "Scope",
|
|
70
|
+
scopeMine: "Mine",
|
|
71
|
+
scopeTenant: "Tenant",
|
|
67
72
|
reportModeTitle: "Report mode is active",
|
|
68
73
|
reportModeDescription: "Click a highlighted section to capture the broken surface.",
|
|
69
74
|
reportModeCancelAction: "Cancel",
|
|
@@ -84,12 +89,15 @@ var defaultIssueReportingCopy = {
|
|
|
84
89
|
replyAction: "Reply",
|
|
85
90
|
hydrateLoading: "Loading the latest issue details...",
|
|
86
91
|
hydrateFailed: "Failed to load the latest issue details.",
|
|
87
|
-
retryAction: "Retry"
|
|
92
|
+
retryAction: "Retry",
|
|
93
|
+
originHumanLabel: "Human",
|
|
94
|
+
originMachineLabel: "Machine",
|
|
95
|
+
machineOriginFallback: "system"
|
|
88
96
|
};
|
|
89
97
|
var issueReportingKeys = {
|
|
90
98
|
all: ["spaps-issue-reporting"],
|
|
91
|
-
status: () => [...issueReportingKeys.all, "status"],
|
|
92
|
-
history: () => [...issueReportingKeys.all, "history"],
|
|
99
|
+
status: (scope) => [...issueReportingKeys.all, "status", scope],
|
|
100
|
+
history: (scope) => [...issueReportingKeys.all, "history", scope],
|
|
93
101
|
detail: (issueReportId) => [...issueReportingKeys.all, "detail", issueReportId]
|
|
94
102
|
};
|
|
95
103
|
function resolvePageUrl(getPageUrl) {
|
|
@@ -101,6 +109,12 @@ function resolvePageUrl(getPageUrl) {
|
|
|
101
109
|
}
|
|
102
110
|
return `${window.location.pathname}${window.location.search}${window.location.hash}`;
|
|
103
111
|
}
|
|
112
|
+
function resolveInitialScope(defaultScope, allowTenantScope) {
|
|
113
|
+
if (allowTenantScope && defaultScope === "tenant") {
|
|
114
|
+
return "tenant";
|
|
115
|
+
}
|
|
116
|
+
return "mine";
|
|
117
|
+
}
|
|
104
118
|
function normalizeTarget(target, getPageUrl) {
|
|
105
119
|
if (typeof target === "string") {
|
|
106
120
|
const normalized = target.trim();
|
|
@@ -144,10 +158,10 @@ function isClosedIssueStatus(status) {
|
|
|
144
158
|
return status === "resolved" || status === "ignored";
|
|
145
159
|
}
|
|
146
160
|
function filterIssueReports(issues, filter) {
|
|
147
|
-
if (filter === "
|
|
161
|
+
if (filter === "unresolved") {
|
|
148
162
|
return issues.filter((issue) => isOpenIssueStatus(issue.status));
|
|
149
163
|
}
|
|
150
|
-
if (filter === "
|
|
164
|
+
if (filter === "resolved") {
|
|
151
165
|
return issues.filter((issue) => isClosedIssueStatus(issue.status));
|
|
152
166
|
}
|
|
153
167
|
return issues;
|
|
@@ -213,20 +227,20 @@ function useIssueReporting() {
|
|
|
213
227
|
return context;
|
|
214
228
|
}
|
|
215
229
|
function useIssueReportingStatus() {
|
|
216
|
-
const { client, isEligible } = useIssueReporting();
|
|
230
|
+
const { client, isEligible, scope } = useIssueReporting();
|
|
217
231
|
return useQuery({
|
|
218
|
-
queryKey: issueReportingKeys.status(),
|
|
219
|
-
queryFn: () => client.issueReporting.getStatus(),
|
|
232
|
+
queryKey: issueReportingKeys.status(scope),
|
|
233
|
+
queryFn: () => client.issueReporting.getStatus({ scope }),
|
|
220
234
|
enabled: isEligible,
|
|
221
235
|
retry: false,
|
|
222
236
|
staleTime: 3e4
|
|
223
237
|
});
|
|
224
238
|
}
|
|
225
239
|
function useIssueReportingHistory(filter = "all") {
|
|
226
|
-
const { client, isEligible } = useIssueReporting();
|
|
240
|
+
const { client, isEligible, scope } = useIssueReporting();
|
|
227
241
|
const query = useQuery({
|
|
228
|
-
queryKey: issueReportingKeys.history(),
|
|
229
|
-
queryFn: () => client.issueReporting.list({ limit: LIST_LIMIT, offset: 0 }),
|
|
242
|
+
queryKey: issueReportingKeys.history(scope),
|
|
243
|
+
queryFn: () => client.issueReporting.list({ limit: LIST_LIMIT, offset: 0, scope }),
|
|
230
244
|
enabled: isEligible,
|
|
231
245
|
retry: false
|
|
232
246
|
});
|
|
@@ -237,7 +251,7 @@ function useIssueReportingHistory(filter = "all") {
|
|
|
237
251
|
return {
|
|
238
252
|
...query,
|
|
239
253
|
items,
|
|
240
|
-
total: items.length
|
|
254
|
+
total: query.data?.total ?? items.length
|
|
241
255
|
};
|
|
242
256
|
}
|
|
243
257
|
function useIssueReportingMutations() {
|
|
@@ -280,7 +294,10 @@ function IssueReportingProvider({
|
|
|
280
294
|
client,
|
|
281
295
|
isEligible,
|
|
282
296
|
reporterRoleHint,
|
|
297
|
+
principalId,
|
|
283
298
|
getPageUrl,
|
|
299
|
+
defaultScope,
|
|
300
|
+
allowTenantScope = false,
|
|
284
301
|
copy,
|
|
285
302
|
children
|
|
286
303
|
}) {
|
|
@@ -295,6 +312,12 @@ function IssueReportingProvider({
|
|
|
295
312
|
const [isReportMode, setIsReportMode] = useState(false);
|
|
296
313
|
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
|
|
297
314
|
const [modalState, setModalState] = useState(initialModalState);
|
|
315
|
+
const [scope, setScope] = useState(
|
|
316
|
+
() => resolveInitialScope(defaultScope, allowTenantScope)
|
|
317
|
+
);
|
|
318
|
+
useEffect(() => {
|
|
319
|
+
setScope(resolveInitialScope(defaultScope, allowTenantScope));
|
|
320
|
+
}, [allowTenantScope, defaultScope]);
|
|
298
321
|
const closePopover = useCallback(() => {
|
|
299
322
|
setIsPopoverOpen(false);
|
|
300
323
|
}, []);
|
|
@@ -388,6 +411,7 @@ function IssueReportingProvider({
|
|
|
388
411
|
client,
|
|
389
412
|
isEligible,
|
|
390
413
|
reporterRoleHint,
|
|
414
|
+
principalId: principalId ?? null,
|
|
391
415
|
copy: mergedCopy,
|
|
392
416
|
isReportMode,
|
|
393
417
|
enterReportMode,
|
|
@@ -399,9 +423,13 @@ function IssueReportingProvider({
|
|
|
399
423
|
modalState,
|
|
400
424
|
closeModal,
|
|
401
425
|
openExistingIssueModal,
|
|
402
|
-
retryModalHydration
|
|
426
|
+
retryModalHydration,
|
|
427
|
+
scope,
|
|
428
|
+
setScope,
|
|
429
|
+
allowTenantScope
|
|
403
430
|
}),
|
|
404
431
|
[
|
|
432
|
+
allowTenantScope,
|
|
405
433
|
cancelReportMode,
|
|
406
434
|
client,
|
|
407
435
|
closeModal,
|
|
@@ -414,8 +442,10 @@ function IssueReportingProvider({
|
|
|
414
442
|
modalState,
|
|
415
443
|
openExistingIssueModal,
|
|
416
444
|
openPopover,
|
|
445
|
+
principalId,
|
|
417
446
|
reporterRoleHint,
|
|
418
447
|
retryModalHydration,
|
|
448
|
+
scope,
|
|
419
449
|
selectPanel
|
|
420
450
|
]
|
|
421
451
|
);
|
|
@@ -446,6 +476,47 @@ function formatRelativeTime(timestamp) {
|
|
|
446
476
|
addSuffix: true
|
|
447
477
|
});
|
|
448
478
|
}
|
|
479
|
+
function resolveErrorMessage(error, fallback) {
|
|
480
|
+
if (error instanceof Error && error.message.trim()) {
|
|
481
|
+
return error.message;
|
|
482
|
+
}
|
|
483
|
+
if (error && typeof error === "object" && "message" in error && typeof error.message === "string" && error.message.trim()) {
|
|
484
|
+
return error.message;
|
|
485
|
+
}
|
|
486
|
+
return fallback;
|
|
487
|
+
}
|
|
488
|
+
function resolveReporterId(issue) {
|
|
489
|
+
return issue.reporter_principal_id ?? issue.reporter_user_id ?? null;
|
|
490
|
+
}
|
|
491
|
+
function resolveReporterType(issue) {
|
|
492
|
+
if (issue.reporter_principal_type) {
|
|
493
|
+
return issue.reporter_principal_type;
|
|
494
|
+
}
|
|
495
|
+
if (issue.machine_principal_id || issue.agent_id) {
|
|
496
|
+
return "machine";
|
|
497
|
+
}
|
|
498
|
+
return "human";
|
|
499
|
+
}
|
|
500
|
+
function getIssueAction(issue, principalId) {
|
|
501
|
+
if (!principalId) {
|
|
502
|
+
return null;
|
|
503
|
+
}
|
|
504
|
+
if (resolveReporterType(issue) !== "human") {
|
|
505
|
+
return null;
|
|
506
|
+
}
|
|
507
|
+
if (resolveReporterId(issue) !== principalId) {
|
|
508
|
+
return null;
|
|
509
|
+
}
|
|
510
|
+
return isClosedIssueStatus(issue.status) ? "reply" : "edit";
|
|
511
|
+
}
|
|
512
|
+
function getIssueOriginText(issue, copy) {
|
|
513
|
+
if (resolveReporterType(issue) === "machine") {
|
|
514
|
+
const provenance = issue.agent_id ?? issue.machine_principal_id ?? issue.reporter_display_name ?? copy.machineOriginFallback;
|
|
515
|
+
return `${copy.originMachineLabel} \xB7 ${provenance}`;
|
|
516
|
+
}
|
|
517
|
+
const humanName = issue.reporter_display_name?.trim();
|
|
518
|
+
return humanName ? `${copy.originHumanLabel} \xB7 ${humanName}` : copy.originHumanLabel;
|
|
519
|
+
}
|
|
449
520
|
function IssueReportModeBanner() {
|
|
450
521
|
const { copy } = useIssueReporting();
|
|
451
522
|
const reportMode = useReportMode();
|
|
@@ -471,7 +542,7 @@ function IssueList({
|
|
|
471
542
|
onEdit,
|
|
472
543
|
onReply
|
|
473
544
|
}) {
|
|
474
|
-
const { copy } = useIssueReporting();
|
|
545
|
+
const { copy, principalId } = useIssueReporting();
|
|
475
546
|
const history = useIssueReportingHistory(filter);
|
|
476
547
|
if (history.isPending) {
|
|
477
548
|
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-2xl border border-slate-200 bg-slate-50 px-4 py-5 text-sm text-slate-600", children: [
|
|
@@ -494,19 +565,18 @@ function IssueList({
|
|
|
494
565
|
] });
|
|
495
566
|
}
|
|
496
567
|
if (history.items.length === 0) {
|
|
497
|
-
const emptyMessage = filter === "
|
|
568
|
+
const emptyMessage = filter === "unresolved" ? copy.emptyUnresolved : filter === "resolved" ? copy.emptyResolved : copy.emptyAll;
|
|
498
569
|
return /* @__PURE__ */ jsx2("div", { className: "rounded-2xl border border-dashed border-slate-200 bg-slate-50 px-4 py-8 text-center text-sm text-slate-500", children: emptyMessage });
|
|
499
570
|
}
|
|
500
571
|
return /* @__PURE__ */ jsx2("div", { className: "max-h-80 space-y-2 overflow-y-auto pr-1", children: history.items.map((issue) => {
|
|
501
|
-
const
|
|
502
|
-
const
|
|
503
|
-
const onAction = () => isClosed ? onReply(issue.id) : onEdit(issue.id);
|
|
572
|
+
const action = getIssueAction(issue, principalId);
|
|
573
|
+
const onAction = action === "edit" ? () => onEdit(issue.id) : action === "reply" ? () => onReply(issue.id) : null;
|
|
504
574
|
return /* @__PURE__ */ jsx2(
|
|
505
575
|
"div",
|
|
506
576
|
{
|
|
507
577
|
className: "rounded-2xl border border-slate-200 bg-white px-3 py-3 shadow-sm",
|
|
508
578
|
children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
|
|
509
|
-
/* @__PURE__ */ jsx2("div", { className: "mt-0.5 flex-shrink-0", children:
|
|
579
|
+
/* @__PURE__ */ jsx2("div", { className: "mt-0.5 flex-shrink-0", children: isClosedIssueStatus(issue.status) ? /* @__PURE__ */ jsx2(CheckCircle, { className: "h-4 w-4 text-emerald-600", weight: "fill" }) : /* @__PURE__ */ jsx2(Circle, { className: "h-4 w-4 text-rose-600", weight: "fill" }) }),
|
|
510
580
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
511
581
|
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2", children: [
|
|
512
582
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
@@ -522,18 +592,19 @@ function IssueList({
|
|
|
522
592
|
children: getIssueStatusBadgeLabel(issue.status)
|
|
523
593
|
}
|
|
524
594
|
),
|
|
595
|
+
/* @__PURE__ */ jsx2("span", { className: "rounded-full bg-slate-100 px-2 py-0.5 text-[11px] font-medium text-slate-600", children: getIssueOriginText(issue, copy) }),
|
|
525
596
|
/* @__PURE__ */ jsx2("span", { className: "text-xs text-slate-400", children: formatRelativeTime(issue.updated_at) })
|
|
526
597
|
] })
|
|
527
598
|
] }),
|
|
528
|
-
/* @__PURE__ */ jsx2(
|
|
599
|
+
action && onAction ? /* @__PURE__ */ jsx2(
|
|
529
600
|
"button",
|
|
530
601
|
{
|
|
531
602
|
type: "button",
|
|
532
603
|
className: "rounded-full border border-slate-200 px-3 py-1 text-xs font-medium text-slate-700 transition hover:bg-slate-50",
|
|
533
604
|
onClick: onAction,
|
|
534
|
-
children:
|
|
605
|
+
children: action === "reply" ? copy.replyAction : copy.editAction
|
|
535
606
|
}
|
|
536
|
-
)
|
|
607
|
+
) : null
|
|
537
608
|
] }),
|
|
538
609
|
/* @__PURE__ */ jsx2("p", { className: "mt-2 text-sm text-slate-600", children: truncate(issue.note) }),
|
|
539
610
|
/* @__PURE__ */ jsx2("div", { className: "mt-2 truncate text-xs text-slate-400", children: issue.target.page_url })
|
|
@@ -552,7 +623,10 @@ function IssueReportPopover({ children }) {
|
|
|
552
623
|
openPopover,
|
|
553
624
|
closePopover,
|
|
554
625
|
enterReportMode,
|
|
555
|
-
openExistingIssueModal
|
|
626
|
+
openExistingIssueModal,
|
|
627
|
+
scope,
|
|
628
|
+
setScope,
|
|
629
|
+
allowTenantScope
|
|
556
630
|
} = useIssueReporting();
|
|
557
631
|
const history = useIssueReportingHistory("all");
|
|
558
632
|
const status = useIssueReportingStatus();
|
|
@@ -560,11 +634,12 @@ function IssueReportPopover({ children }) {
|
|
|
560
634
|
const counts = useMemo2(
|
|
561
635
|
() => ({
|
|
562
636
|
all: allItems.length,
|
|
563
|
-
|
|
564
|
-
|
|
637
|
+
unresolved: filterIssueReports(allItems, "unresolved").length,
|
|
638
|
+
resolved: filterIssueReports(allItems, "resolved").length
|
|
565
639
|
}),
|
|
566
640
|
[allItems]
|
|
567
641
|
);
|
|
642
|
+
const statusSummary = status.data ? `${status.data.open_count} open \xB7 ${status.data.recent_resolved_count} recently resolved` : "Status reflects the active scope.";
|
|
568
643
|
return /* @__PURE__ */ jsxs(
|
|
569
644
|
Popover.Root,
|
|
570
645
|
{
|
|
@@ -583,11 +658,7 @@ function IssueReportPopover({ children }) {
|
|
|
583
658
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
|
|
584
659
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
585
660
|
/* @__PURE__ */ jsx2("h3", { className: "text-sm font-semibold text-slate-900", children: copy.popoverTitle }),
|
|
586
|
-
|
|
587
|
-
status.data.open_count,
|
|
588
|
-
" open issue",
|
|
589
|
-
status.data.open_count === 1 ? "" : "s"
|
|
590
|
-
] }) : null
|
|
661
|
+
/* @__PURE__ */ jsx2("p", { className: "mt-1 text-xs text-slate-500", children: statusSummary })
|
|
591
662
|
] }),
|
|
592
663
|
/* @__PURE__ */ jsx2(
|
|
593
664
|
"button",
|
|
@@ -602,10 +673,41 @@ function IssueReportPopover({ children }) {
|
|
|
602
673
|
}
|
|
603
674
|
)
|
|
604
675
|
] }),
|
|
676
|
+
status.error ? /* @__PURE__ */ jsxs("div", { className: "space-y-3 rounded-2xl border border-rose-200 bg-rose-50 px-4 py-4 text-sm text-rose-700", children: [
|
|
677
|
+
/* @__PURE__ */ jsx2("div", { children: status.error.message || copy.statusLoadFailed }),
|
|
678
|
+
/* @__PURE__ */ jsx2(
|
|
679
|
+
"button",
|
|
680
|
+
{
|
|
681
|
+
type: "button",
|
|
682
|
+
className: "rounded-full border border-rose-300 px-3 py-1 font-medium transition hover:bg-rose-100",
|
|
683
|
+
onClick: () => status.refetch(),
|
|
684
|
+
children: copy.retryAction
|
|
685
|
+
}
|
|
686
|
+
)
|
|
687
|
+
] }) : null,
|
|
688
|
+
allowTenantScope ? /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
689
|
+
/* @__PURE__ */ jsx2("div", { className: "text-[11px] font-medium uppercase tracking-wide text-slate-500", children: copy.scopeLabel }),
|
|
690
|
+
/* @__PURE__ */ jsx2("div", { className: "flex gap-2", children: [
|
|
691
|
+
["tenant", copy.scopeTenant],
|
|
692
|
+
["mine", copy.scopeMine]
|
|
693
|
+
].map(([value, label]) => /* @__PURE__ */ jsx2(
|
|
694
|
+
"button",
|
|
695
|
+
{
|
|
696
|
+
type: "button",
|
|
697
|
+
className: cn(
|
|
698
|
+
"rounded-full px-3 py-1.5 text-xs font-medium transition",
|
|
699
|
+
scope === value ? "bg-slate-900 text-white" : "bg-slate-100 text-slate-600 hover:bg-slate-200"
|
|
700
|
+
),
|
|
701
|
+
onClick: () => setScope(value),
|
|
702
|
+
children: label
|
|
703
|
+
},
|
|
704
|
+
value
|
|
705
|
+
)) })
|
|
706
|
+
] }) : null,
|
|
605
707
|
/* @__PURE__ */ jsx2("div", { className: "flex gap-2", children: [
|
|
606
708
|
["all", copy.filtersAll, counts.all],
|
|
607
|
-
["
|
|
608
|
-
["
|
|
709
|
+
["unresolved", copy.filtersUnresolved, counts.unresolved],
|
|
710
|
+
["resolved", copy.filtersResolved, counts.resolved]
|
|
609
711
|
].map(([value, label, count]) => /* @__PURE__ */ jsxs(
|
|
610
712
|
"button",
|
|
611
713
|
{
|
|
@@ -650,7 +752,7 @@ function IssueReportModal() {
|
|
|
650
752
|
const [note, setNote] = useState2("");
|
|
651
753
|
const [submitError, setSubmitError] = useState2(null);
|
|
652
754
|
const { isOpen, mode, issue, target, isHydrating, error } = modalState;
|
|
653
|
-
|
|
755
|
+
useEffect2(() => {
|
|
654
756
|
if (!isOpen) {
|
|
655
757
|
setNote("");
|
|
656
758
|
setSubmitError(null);
|
|
@@ -693,9 +795,7 @@ function IssueReportModal() {
|
|
|
693
795
|
}
|
|
694
796
|
closeModal();
|
|
695
797
|
} catch (submissionError) {
|
|
696
|
-
setSubmitError(
|
|
697
|
-
submissionError instanceof Error ? submissionError.message : "Failed to submit issue report"
|
|
698
|
-
);
|
|
798
|
+
setSubmitError(resolveErrorMessage(submissionError, "Failed to submit issue report"));
|
|
699
799
|
}
|
|
700
800
|
};
|
|
701
801
|
return /* @__PURE__ */ jsx2(Dialog.Root, { open: isOpen, onOpenChange: (open) => !open && closeModal(), children: /* @__PURE__ */ jsxs(Dialog.Portal, { children: [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spaps-issue-reporting-react",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Shared React issue-reporting UI for Sweet Potato platform consumers",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"@radix-ui/react-dialog": "^1.1.15",
|
|
39
39
|
"@radix-ui/react-popover": "^1.1.15",
|
|
40
40
|
"date-fns": "^4.1.0",
|
|
41
|
-
"spaps-types": "^1.1.
|
|
41
|
+
"spaps-types": "^1.1.2"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"@tanstack/react-query": ">=5.0.0",
|
|
@@ -72,4 +72,4 @@
|
|
|
72
72
|
"engines": {
|
|
73
73
|
"node": ">=18.0.0"
|
|
74
74
|
}
|
|
75
|
-
}
|
|
75
|
+
}
|