spaps-issue-reporting-react 0.1.2 → 0.1.3
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 +4 -0
- package/README.md +27 -3
- package/dist/index.d.mts +21 -3
- package/dist/index.d.ts +21 -3
- package/dist/index.js +302 -34
- package/dist/index.mjs +302 -34
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -17,9 +17,9 @@ This package targets `Node.js >=18` and React 18+.
|
|
|
17
17
|
| Need | Package gives you |
|
|
18
18
|
| --- | --- |
|
|
19
19
|
| A visible issue-report entry point | Floating button with open/recent state |
|
|
20
|
-
| A guided reporting flow |
|
|
20
|
+
| A guided reporting flow | Page-first create flow, optional section picking, create/edit/reply modal |
|
|
21
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
|
+
| App-level control | Eligibility, reporter identity, scope, page policy, and copy stay in your app |
|
|
23
23
|
|
|
24
24
|
## Quick Start
|
|
25
25
|
|
|
@@ -27,6 +27,7 @@ This package targets `Node.js >=18` and React 18+.
|
|
|
27
27
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
28
28
|
import {
|
|
29
29
|
FloatingIssueReportButton,
|
|
30
|
+
IssueReportingPageConfig,
|
|
30
31
|
IssueReportingProvider,
|
|
31
32
|
ReportableSection,
|
|
32
33
|
} from "spaps-issue-reporting-react";
|
|
@@ -45,6 +46,8 @@ export function AppShell() {
|
|
|
45
46
|
allowTenantScope
|
|
46
47
|
defaultScope="mine"
|
|
47
48
|
>
|
|
49
|
+
<IssueReportingPageConfig createMode="surface_preferred" />
|
|
50
|
+
|
|
48
51
|
<ReportableSection
|
|
49
52
|
reportableName={{
|
|
50
53
|
componentKey: "patient_chart",
|
|
@@ -70,6 +73,7 @@ export function AppShell() {
|
|
|
70
73
|
- Eligibility rules such as feature flags, account state, or role checks.
|
|
71
74
|
- The current principal ID and optional role hint passed into the provider.
|
|
72
75
|
- Whether users can switch between `mine` and `tenant` queue scope.
|
|
76
|
+
- Whether a page defaults to `general_page`, `surface_required`, or `surface_preferred` reporting.
|
|
73
77
|
- Styling integration if your build strips package utility classes.
|
|
74
78
|
- Any app-specific copy overrides.
|
|
75
79
|
- Origin registration on the owning SPAPS application if the browser calls SPAPS directly with a publishable key.
|
|
@@ -81,9 +85,10 @@ For direct browser integrations, `allowed_origins` is stored on the SPAPS `appli
|
|
|
81
85
|
| Export | Purpose |
|
|
82
86
|
| --- | --- |
|
|
83
87
|
| `IssueReportingProvider` | Owns queries, modal state, copy, scope, and report-mode behavior |
|
|
88
|
+
| `IssueReportingPageConfig` | Per-page override for `general_page`, `surface_required`, or `surface_preferred` create policy |
|
|
84
89
|
| `FloatingIssueReportButton` | Renders the floating entry point, popover, and modal |
|
|
85
90
|
| `ReportableSection` | Marks a region as selectable when report mode is active |
|
|
86
|
-
| `useIssueReporting` | Full provider state, including `enterReportMode()` |
|
|
91
|
+
| `useIssueReporting` | Full provider state, including `startNewIssue()`, `openPageIssueModal()`, and `enterReportMode()` |
|
|
87
92
|
| `useIssueReportingStatus` | Query helper for summary state |
|
|
88
93
|
| `useIssueReportingHistory` | Query helper for filtered history lists |
|
|
89
94
|
| `useIssueReportingMutations` | Mutation helpers for create, update, and reply flows |
|
|
@@ -111,10 +116,18 @@ npm test
|
|
|
111
116
|
|
|
112
117
|
Make sure `FloatingIssueReportButton` is rendered inside `IssueReportingProvider` and that `isEligible` is `true`.
|
|
113
118
|
|
|
119
|
+
### I want page-level reporting without wrapping every widget
|
|
120
|
+
|
|
121
|
+
That is the default. `Report This Page` opens immediately and captures the current route plus the inventory of visible registered reportable sections on the active page.
|
|
122
|
+
|
|
114
123
|
### Report mode turns on, but sections do not respond
|
|
115
124
|
|
|
116
125
|
Wrap the target UI in `ReportableSection`, or call `useIssueReporting().selectPanel(...)` from a custom control.
|
|
117
126
|
|
|
127
|
+
### This page should force a specific surface selection
|
|
128
|
+
|
|
129
|
+
Render `IssueReportingPageConfig` with `createMode="surface_required"` inside that page's visible provider subtree. Hidden mounted routes, tabs, or panels do not affect the active page's policy.
|
|
130
|
+
|
|
118
131
|
### Tenant scope never appears
|
|
119
132
|
|
|
120
133
|
Set `allowTenantScope={true}`. The provider defaults to `mine`.
|
|
@@ -139,6 +152,10 @@ No. You only need a client object that implements the `issueReporting` methods e
|
|
|
139
152
|
|
|
140
153
|
Yes. Use `useIssueReporting().enterReportMode()` inside the provider tree.
|
|
141
154
|
|
|
155
|
+
### Can I open a page-level report from my own button?
|
|
156
|
+
|
|
157
|
+
Yes. Use `useIssueReporting().openPageIssueModal()` or `useIssueReporting().startNewIssue()`.
|
|
158
|
+
|
|
142
159
|
### Can I report plain strings instead of structured descriptors?
|
|
143
160
|
|
|
144
161
|
Yes. `reportableName` accepts either a string or a `{ componentKey, componentLabel, ... }` object.
|
|
@@ -155,6 +172,13 @@ No. It only renders scope choices that your app explicitly allows.
|
|
|
155
172
|
|
|
156
173
|
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
174
|
|
|
175
|
+
## Metadata
|
|
176
|
+
|
|
177
|
+
- `package_name`: `spaps-issue-reporting-react`
|
|
178
|
+
- `latest_version`: `0.1.1`
|
|
179
|
+
- `minimum_runtime`: `Node.js >=18.0.0`
|
|
180
|
+
- `api_base_url`: `https://api.sweetpotato.dev`
|
|
181
|
+
|
|
158
182
|
## About Contributions
|
|
159
183
|
|
|
160
184
|
> *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.
|
package/dist/index.d.mts
CHANGED
|
@@ -31,11 +31,14 @@ interface ReportableTargetDescriptor {
|
|
|
31
31
|
metadata?: Record<string, unknown>;
|
|
32
32
|
}
|
|
33
33
|
type ReportableInput = string | ReportableTargetDescriptor;
|
|
34
|
+
type IssueReportingCreateMode = "general_page" | "surface_required" | "surface_preferred";
|
|
34
35
|
type IssueHistoryFilter = "all" | "unresolved" | "resolved";
|
|
35
36
|
interface IssueReportingCopy {
|
|
36
37
|
entryAriaLabel: string;
|
|
37
38
|
popoverTitle: string;
|
|
38
39
|
reportNewAction: string;
|
|
40
|
+
reportPageAction: string;
|
|
41
|
+
reportSpecificAction: string;
|
|
39
42
|
filtersAll: string;
|
|
40
43
|
filtersUnresolved: string;
|
|
41
44
|
filtersResolved: string;
|
|
@@ -52,6 +55,9 @@ interface IssueReportingCopy {
|
|
|
52
55
|
reportModeTitle: string;
|
|
53
56
|
reportModeDescription: string;
|
|
54
57
|
reportModeCancelAction: string;
|
|
58
|
+
generalPageTargetLabel: string;
|
|
59
|
+
surfaceRequiredDescription: string;
|
|
60
|
+
specificSectionUnavailableDescription: string;
|
|
55
61
|
createTitlePrefix: string;
|
|
56
62
|
editTitlePrefix: string;
|
|
57
63
|
replyTitlePrefix: string;
|
|
@@ -82,6 +88,7 @@ interface IssueReportingProviderProps {
|
|
|
82
88
|
getPageUrl?: () => string;
|
|
83
89
|
defaultScope?: IssueReportScope;
|
|
84
90
|
allowTenantScope?: boolean;
|
|
91
|
+
defaultCreateMode?: IssueReportingCreateMode;
|
|
85
92
|
copy?: Partial<IssueReportingCopy>;
|
|
86
93
|
children: ReactNode;
|
|
87
94
|
}
|
|
@@ -89,6 +96,9 @@ interface FloatingIssueReportButtonProps {
|
|
|
89
96
|
className?: string;
|
|
90
97
|
positionClassName?: string;
|
|
91
98
|
}
|
|
99
|
+
interface IssueReportingPageConfigProps {
|
|
100
|
+
createMode: IssueReportingCreateMode;
|
|
101
|
+
}
|
|
92
102
|
|
|
93
103
|
declare function FloatingIssueReportButton({ className, positionClassName, }: FloatingIssueReportButtonProps): react_jsx_runtime.JSX.Element | null;
|
|
94
104
|
declare function ReportableSection({ reportableName, children, className, as: Component, }: {
|
|
@@ -96,7 +106,7 @@ declare function ReportableSection({ reportableName, children, className, as: Co
|
|
|
96
106
|
children: React__default.ReactNode;
|
|
97
107
|
className?: string;
|
|
98
108
|
as?: keyof JSX.IntrinsicElements;
|
|
99
|
-
}):
|
|
109
|
+
}): React__default.ReactElement<any, string | React__default.JSXElementConstructor<any>>;
|
|
100
110
|
|
|
101
111
|
type ModalMode = "create" | "edit" | "reply";
|
|
102
112
|
type ResolvedTarget = {
|
|
@@ -122,9 +132,12 @@ type IssueReportingContextValue = {
|
|
|
122
132
|
principalId: string | null;
|
|
123
133
|
copy: IssueReportingCopy;
|
|
124
134
|
isReportMode: boolean;
|
|
135
|
+
hasRegisteredTargets: boolean;
|
|
125
136
|
enterReportMode: () => void;
|
|
126
137
|
cancelReportMode: () => void;
|
|
127
138
|
selectPanel: (target: ReportableInput) => void;
|
|
139
|
+
openPageIssueModal: () => void;
|
|
140
|
+
startNewIssue: () => void;
|
|
128
141
|
isPopoverOpen: boolean;
|
|
129
142
|
openPopover: () => void;
|
|
130
143
|
closePopover: () => void;
|
|
@@ -135,6 +148,7 @@ type IssueReportingContextValue = {
|
|
|
135
148
|
scope: IssueReportScope;
|
|
136
149
|
setScope: (scope: IssueReportScope) => void;
|
|
137
150
|
allowTenantScope: boolean;
|
|
151
|
+
createMode: IssueReportingCreateMode;
|
|
138
152
|
};
|
|
139
153
|
declare const defaultIssueReportingCopy: IssueReportingCopy;
|
|
140
154
|
declare const issueReportingKeys: {
|
|
@@ -155,6 +169,7 @@ declare function getEntryPointState(status?: {
|
|
|
155
169
|
declare function getEntryPointClassName(state: "open" | "recent_resolved" | "neutral"): string;
|
|
156
170
|
declare function getIssueNoteLengthMessage(note: string, copy: IssueReportingCopy): string;
|
|
157
171
|
declare function useIssueReporting(): IssueReportingContextValue;
|
|
172
|
+
declare function IssueReportingPageConfig({ createMode, }: IssueReportingPageConfigProps): react_jsx_runtime.JSX.Element;
|
|
158
173
|
declare function useIssueReportingStatus(): _tanstack_react_query.UseQueryResult<spaps_types.IssueReportStatusResult, Error>;
|
|
159
174
|
declare function useIssueReportingHistory(filter?: IssueHistoryFilter): {
|
|
160
175
|
items: IssueReport[];
|
|
@@ -347,14 +362,17 @@ declare function useIssueReportingMutations(): {
|
|
|
347
362
|
reporterRoleHint?: string;
|
|
348
363
|
}, unknown>;
|
|
349
364
|
};
|
|
350
|
-
declare function IssueReportingProvider({ client, isEligible, reporterRoleHint, principalId, getPageUrl, defaultScope, allowTenantScope, copy, children, }: IssueReportingProviderProps): react_jsx_runtime.JSX.Element;
|
|
365
|
+
declare function IssueReportingProvider({ client, isEligible, reporterRoleHint, principalId, getPageUrl, defaultScope, allowTenantScope, defaultCreateMode, copy, children, }: IssueReportingProviderProps): react_jsx_runtime.JSX.Element;
|
|
351
366
|
|
|
352
367
|
interface ReportModeContextValue {
|
|
353
368
|
isReportMode: boolean;
|
|
354
369
|
selectPanel: (target: ReportableInput) => void;
|
|
355
370
|
cancelReportMode: () => void;
|
|
371
|
+
hasRegisteredTargets: boolean;
|
|
372
|
+
registerTarget: (id: symbol, target: ReportableInput, getElement: () => Element | null) => void;
|
|
373
|
+
unregisterTarget: (id: symbol) => void;
|
|
356
374
|
}
|
|
357
375
|
declare const ReportModeContext: React.Context<ReportModeContextValue | null>;
|
|
358
376
|
declare function useReportMode(): ReportModeContextValue | null;
|
|
359
377
|
|
|
360
|
-
export { FloatingIssueReportButton, type FloatingIssueReportButtonProps, type IssueHistoryFilter, type IssueReportingClient, type IssueReportingCopy, 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 };
|
|
378
|
+
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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -31,11 +31,14 @@ interface ReportableTargetDescriptor {
|
|
|
31
31
|
metadata?: Record<string, unknown>;
|
|
32
32
|
}
|
|
33
33
|
type ReportableInput = string | ReportableTargetDescriptor;
|
|
34
|
+
type IssueReportingCreateMode = "general_page" | "surface_required" | "surface_preferred";
|
|
34
35
|
type IssueHistoryFilter = "all" | "unresolved" | "resolved";
|
|
35
36
|
interface IssueReportingCopy {
|
|
36
37
|
entryAriaLabel: string;
|
|
37
38
|
popoverTitle: string;
|
|
38
39
|
reportNewAction: string;
|
|
40
|
+
reportPageAction: string;
|
|
41
|
+
reportSpecificAction: string;
|
|
39
42
|
filtersAll: string;
|
|
40
43
|
filtersUnresolved: string;
|
|
41
44
|
filtersResolved: string;
|
|
@@ -52,6 +55,9 @@ interface IssueReportingCopy {
|
|
|
52
55
|
reportModeTitle: string;
|
|
53
56
|
reportModeDescription: string;
|
|
54
57
|
reportModeCancelAction: string;
|
|
58
|
+
generalPageTargetLabel: string;
|
|
59
|
+
surfaceRequiredDescription: string;
|
|
60
|
+
specificSectionUnavailableDescription: string;
|
|
55
61
|
createTitlePrefix: string;
|
|
56
62
|
editTitlePrefix: string;
|
|
57
63
|
replyTitlePrefix: string;
|
|
@@ -82,6 +88,7 @@ interface IssueReportingProviderProps {
|
|
|
82
88
|
getPageUrl?: () => string;
|
|
83
89
|
defaultScope?: IssueReportScope;
|
|
84
90
|
allowTenantScope?: boolean;
|
|
91
|
+
defaultCreateMode?: IssueReportingCreateMode;
|
|
85
92
|
copy?: Partial<IssueReportingCopy>;
|
|
86
93
|
children: ReactNode;
|
|
87
94
|
}
|
|
@@ -89,6 +96,9 @@ interface FloatingIssueReportButtonProps {
|
|
|
89
96
|
className?: string;
|
|
90
97
|
positionClassName?: string;
|
|
91
98
|
}
|
|
99
|
+
interface IssueReportingPageConfigProps {
|
|
100
|
+
createMode: IssueReportingCreateMode;
|
|
101
|
+
}
|
|
92
102
|
|
|
93
103
|
declare function FloatingIssueReportButton({ className, positionClassName, }: FloatingIssueReportButtonProps): react_jsx_runtime.JSX.Element | null;
|
|
94
104
|
declare function ReportableSection({ reportableName, children, className, as: Component, }: {
|
|
@@ -96,7 +106,7 @@ declare function ReportableSection({ reportableName, children, className, as: Co
|
|
|
96
106
|
children: React__default.ReactNode;
|
|
97
107
|
className?: string;
|
|
98
108
|
as?: keyof JSX.IntrinsicElements;
|
|
99
|
-
}):
|
|
109
|
+
}): React__default.ReactElement<any, string | React__default.JSXElementConstructor<any>>;
|
|
100
110
|
|
|
101
111
|
type ModalMode = "create" | "edit" | "reply";
|
|
102
112
|
type ResolvedTarget = {
|
|
@@ -122,9 +132,12 @@ type IssueReportingContextValue = {
|
|
|
122
132
|
principalId: string | null;
|
|
123
133
|
copy: IssueReportingCopy;
|
|
124
134
|
isReportMode: boolean;
|
|
135
|
+
hasRegisteredTargets: boolean;
|
|
125
136
|
enterReportMode: () => void;
|
|
126
137
|
cancelReportMode: () => void;
|
|
127
138
|
selectPanel: (target: ReportableInput) => void;
|
|
139
|
+
openPageIssueModal: () => void;
|
|
140
|
+
startNewIssue: () => void;
|
|
128
141
|
isPopoverOpen: boolean;
|
|
129
142
|
openPopover: () => void;
|
|
130
143
|
closePopover: () => void;
|
|
@@ -135,6 +148,7 @@ type IssueReportingContextValue = {
|
|
|
135
148
|
scope: IssueReportScope;
|
|
136
149
|
setScope: (scope: IssueReportScope) => void;
|
|
137
150
|
allowTenantScope: boolean;
|
|
151
|
+
createMode: IssueReportingCreateMode;
|
|
138
152
|
};
|
|
139
153
|
declare const defaultIssueReportingCopy: IssueReportingCopy;
|
|
140
154
|
declare const issueReportingKeys: {
|
|
@@ -155,6 +169,7 @@ declare function getEntryPointState(status?: {
|
|
|
155
169
|
declare function getEntryPointClassName(state: "open" | "recent_resolved" | "neutral"): string;
|
|
156
170
|
declare function getIssueNoteLengthMessage(note: string, copy: IssueReportingCopy): string;
|
|
157
171
|
declare function useIssueReporting(): IssueReportingContextValue;
|
|
172
|
+
declare function IssueReportingPageConfig({ createMode, }: IssueReportingPageConfigProps): react_jsx_runtime.JSX.Element;
|
|
158
173
|
declare function useIssueReportingStatus(): _tanstack_react_query.UseQueryResult<spaps_types.IssueReportStatusResult, Error>;
|
|
159
174
|
declare function useIssueReportingHistory(filter?: IssueHistoryFilter): {
|
|
160
175
|
items: IssueReport[];
|
|
@@ -347,14 +362,17 @@ declare function useIssueReportingMutations(): {
|
|
|
347
362
|
reporterRoleHint?: string;
|
|
348
363
|
}, unknown>;
|
|
349
364
|
};
|
|
350
|
-
declare function IssueReportingProvider({ client, isEligible, reporterRoleHint, principalId, getPageUrl, defaultScope, allowTenantScope, copy, children, }: IssueReportingProviderProps): react_jsx_runtime.JSX.Element;
|
|
365
|
+
declare function IssueReportingProvider({ client, isEligible, reporterRoleHint, principalId, getPageUrl, defaultScope, allowTenantScope, defaultCreateMode, copy, children, }: IssueReportingProviderProps): react_jsx_runtime.JSX.Element;
|
|
351
366
|
|
|
352
367
|
interface ReportModeContextValue {
|
|
353
368
|
isReportMode: boolean;
|
|
354
369
|
selectPanel: (target: ReportableInput) => void;
|
|
355
370
|
cancelReportMode: () => void;
|
|
371
|
+
hasRegisteredTargets: boolean;
|
|
372
|
+
registerTarget: (id: symbol, target: ReportableInput, getElement: () => Element | null) => void;
|
|
373
|
+
unregisterTarget: (id: symbol) => void;
|
|
356
374
|
}
|
|
357
375
|
declare const ReportModeContext: React.Context<ReportModeContextValue | null>;
|
|
358
376
|
declare function useReportMode(): ReportModeContextValue | null;
|
|
359
377
|
|
|
360
|
-
export { FloatingIssueReportButton, type FloatingIssueReportButtonProps, type IssueHistoryFilter, type IssueReportingClient, type IssueReportingCopy, 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 };
|
|
378
|
+
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 };
|
package/dist/index.js
CHANGED
|
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
FloatingIssueReportButton: () => FloatingIssueReportButton,
|
|
34
|
+
IssueReportingPageConfig: () => IssueReportingPageConfig,
|
|
34
35
|
IssueReportingProvider: () => IssueReportingProvider,
|
|
35
36
|
ReportModeContext: () => ReportModeContext,
|
|
36
37
|
ReportableSection: () => ReportableSection,
|
|
@@ -57,7 +58,7 @@ var Dialog = __toESM(require("@radix-ui/react-dialog"));
|
|
|
57
58
|
var Popover = __toESM(require("@radix-ui/react-popover"));
|
|
58
59
|
var import_react3 = require("@phosphor-icons/react");
|
|
59
60
|
var import_date_fns = require("date-fns");
|
|
60
|
-
var import_react4 = require("react");
|
|
61
|
+
var import_react4 = __toESM(require("react"));
|
|
61
62
|
|
|
62
63
|
// src/provider.tsx
|
|
63
64
|
var import_react2 = require("react");
|
|
@@ -89,10 +90,24 @@ var initialModalState = {
|
|
|
89
90
|
var IssueReportingContext = (0, import_react2.createContext)(
|
|
90
91
|
void 0
|
|
91
92
|
);
|
|
93
|
+
var IssueReportingPageConfigRegistryContext = (0, import_react2.createContext)(void 0);
|
|
94
|
+
var hiddenMarkerStyle = {
|
|
95
|
+
position: "absolute",
|
|
96
|
+
width: "1px",
|
|
97
|
+
height: "1px",
|
|
98
|
+
padding: 0,
|
|
99
|
+
margin: "-1px",
|
|
100
|
+
overflow: "hidden",
|
|
101
|
+
clip: "rect(0, 0, 0, 0)",
|
|
102
|
+
whiteSpace: "nowrap",
|
|
103
|
+
border: 0
|
|
104
|
+
};
|
|
92
105
|
var defaultIssueReportingCopy = {
|
|
93
106
|
entryAriaLabel: "Report issue",
|
|
94
107
|
popoverTitle: "Issue Reports",
|
|
95
108
|
reportNewAction: "Report New Issue",
|
|
109
|
+
reportPageAction: "Report This Page",
|
|
110
|
+
reportSpecificAction: "Pick Specific Section",
|
|
96
111
|
filtersAll: "All",
|
|
97
112
|
filtersUnresolved: "Unresolved",
|
|
98
113
|
filtersResolved: "Resolved",
|
|
@@ -109,6 +124,9 @@ var defaultIssueReportingCopy = {
|
|
|
109
124
|
reportModeTitle: "Report mode is active",
|
|
110
125
|
reportModeDescription: "Click a highlighted section to capture the broken surface.",
|
|
111
126
|
reportModeCancelAction: "Cancel",
|
|
127
|
+
generalPageTargetLabel: "Current Page",
|
|
128
|
+
surfaceRequiredDescription: "This page requires selecting a specific section before you submit a report.",
|
|
129
|
+
specificSectionUnavailableDescription: "No specific sections are registered on this page right now.",
|
|
112
130
|
createTitlePrefix: "Report Issue",
|
|
113
131
|
editTitlePrefix: "Edit Issue",
|
|
114
132
|
replyTitlePrefix: "Reply to",
|
|
@@ -173,6 +191,44 @@ function normalizeTarget(target, getPageUrl) {
|
|
|
173
191
|
metadata: target.metadata ?? {}
|
|
174
192
|
};
|
|
175
193
|
}
|
|
194
|
+
function summarizeTarget(target) {
|
|
195
|
+
if (typeof target === "string") {
|
|
196
|
+
const normalized = target.trim();
|
|
197
|
+
return {
|
|
198
|
+
component_key: normalized,
|
|
199
|
+
component_label: normalized,
|
|
200
|
+
surface_ref: null,
|
|
201
|
+
metadata: {}
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
const key = target.componentKey.trim();
|
|
205
|
+
return {
|
|
206
|
+
component_key: key,
|
|
207
|
+
component_label: (target.componentLabel ?? key).trim(),
|
|
208
|
+
surface_ref: target.surfaceRef ?? null,
|
|
209
|
+
metadata: target.metadata ?? {}
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
function isElementVisibleForReporting(element) {
|
|
213
|
+
if (!element || typeof window === "undefined") {
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
let current = element;
|
|
217
|
+
while (current) {
|
|
218
|
+
if (current.getAttribute("aria-hidden") === "true") {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
if (current instanceof HTMLElement && (current.hidden || current.inert)) {
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
const computedStyle = window.getComputedStyle(current);
|
|
225
|
+
if (computedStyle.display === "none" || computedStyle.visibility === "hidden" || computedStyle.visibility === "collapse") {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
current = current.parentElement;
|
|
229
|
+
}
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
176
232
|
function normalizeIssueTarget(issue) {
|
|
177
233
|
return {
|
|
178
234
|
component_key: issue.target.component_key,
|
|
@@ -254,6 +310,23 @@ function getIssueNoteLengthMessage(note, copy) {
|
|
|
254
310
|
}
|
|
255
311
|
return `${note.length}/${NOTE_MAX_LENGTH}`;
|
|
256
312
|
}
|
|
313
|
+
function buildGeneralPageTarget(copy, registeredTargets, getPageUrl) {
|
|
314
|
+
return {
|
|
315
|
+
component_key: "general_page",
|
|
316
|
+
component_label: copy.generalPageTargetLabel,
|
|
317
|
+
page_url: resolvePageUrl(getPageUrl),
|
|
318
|
+
surface_ref: null,
|
|
319
|
+
metadata: {
|
|
320
|
+
capture_mode: "general_page",
|
|
321
|
+
registered_target_count: registeredTargets.length,
|
|
322
|
+
...registeredTargets.length > 0 ? {
|
|
323
|
+
registered_targets: registeredTargets.map(
|
|
324
|
+
({ target }) => summarizeTarget(target)
|
|
325
|
+
)
|
|
326
|
+
} : {}
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
}
|
|
257
330
|
function useIssueReporting() {
|
|
258
331
|
const context = (0, import_react2.useContext)(IssueReportingContext);
|
|
259
332
|
if (!context) {
|
|
@@ -263,6 +336,25 @@ function useIssueReporting() {
|
|
|
263
336
|
}
|
|
264
337
|
return context;
|
|
265
338
|
}
|
|
339
|
+
function IssueReportingPageConfig({
|
|
340
|
+
createMode
|
|
341
|
+
}) {
|
|
342
|
+
const registry = (0, import_react2.useContext)(IssueReportingPageConfigRegistryContext);
|
|
343
|
+
const configId = (0, import_react2.useRef)(/* @__PURE__ */ Symbol("issue-reporting-page-config"));
|
|
344
|
+
const markerRef = (0, import_react2.useRef)(null);
|
|
345
|
+
if (!registry) {
|
|
346
|
+
throw new Error(
|
|
347
|
+
"IssueReportingPageConfig must be used within an IssueReportingProvider"
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
(0, import_react2.useEffect)(() => {
|
|
351
|
+
registry.registerPageConfig(configId.current, createMode, () => markerRef.current);
|
|
352
|
+
return () => {
|
|
353
|
+
registry.unregisterPageConfig(configId.current);
|
|
354
|
+
};
|
|
355
|
+
}, [createMode, registry]);
|
|
356
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ref: markerRef, style: hiddenMarkerStyle });
|
|
357
|
+
}
|
|
266
358
|
function useIssueReportingStatus() {
|
|
267
359
|
const { client, isEligible, scope } = useIssueReporting();
|
|
268
360
|
return (0, import_react_query.useQuery)({
|
|
@@ -335,6 +427,7 @@ function IssueReportingProvider({
|
|
|
335
427
|
getPageUrl,
|
|
336
428
|
defaultScope,
|
|
337
429
|
allowTenantScope = false,
|
|
430
|
+
defaultCreateMode = "general_page",
|
|
338
431
|
copy,
|
|
339
432
|
children
|
|
340
433
|
}) {
|
|
@@ -352,6 +445,10 @@ function IssueReportingProvider({
|
|
|
352
445
|
const [scope, setScope] = (0, import_react2.useState)(
|
|
353
446
|
() => resolveInitialScope(defaultScope, allowTenantScope)
|
|
354
447
|
);
|
|
448
|
+
const [pageConfigs, setPageConfigs] = (0, import_react2.useState)([]);
|
|
449
|
+
const [registeredTargets, setRegisteredTargets] = (0, import_react2.useState)(
|
|
450
|
+
[]
|
|
451
|
+
);
|
|
355
452
|
(0, import_react2.useEffect)(() => {
|
|
356
453
|
setScope(resolveInitialScope(defaultScope, allowTenantScope));
|
|
357
454
|
}, [allowTenantScope, defaultScope]);
|
|
@@ -361,6 +458,51 @@ function IssueReportingProvider({
|
|
|
361
458
|
const openPopover = (0, import_react2.useCallback)(() => {
|
|
362
459
|
setIsPopoverOpen(true);
|
|
363
460
|
}, []);
|
|
461
|
+
const openCreateModal = (0, import_react2.useCallback)((target) => {
|
|
462
|
+
setIsPopoverOpen(false);
|
|
463
|
+
setIsReportMode(false);
|
|
464
|
+
setModalState({
|
|
465
|
+
isOpen: true,
|
|
466
|
+
mode: "create",
|
|
467
|
+
issueReportId: null,
|
|
468
|
+
issue: null,
|
|
469
|
+
target,
|
|
470
|
+
error: null,
|
|
471
|
+
isHydrating: false
|
|
472
|
+
});
|
|
473
|
+
}, []);
|
|
474
|
+
const registerPageConfig = (0, import_react2.useCallback)(
|
|
475
|
+
(id, createMode2, getElement) => {
|
|
476
|
+
setPageConfigs((current) => [
|
|
477
|
+
...current.filter((entry) => entry.id !== id),
|
|
478
|
+
{ id, createMode: createMode2, getElement }
|
|
479
|
+
]);
|
|
480
|
+
},
|
|
481
|
+
[]
|
|
482
|
+
);
|
|
483
|
+
const unregisterPageConfig = (0, import_react2.useCallback)((id) => {
|
|
484
|
+
setPageConfigs((current) => current.filter((entry) => entry.id !== id));
|
|
485
|
+
}, []);
|
|
486
|
+
const registerTarget = (0, import_react2.useCallback)(
|
|
487
|
+
(id, target, getElement) => {
|
|
488
|
+
setRegisteredTargets((current) => [
|
|
489
|
+
...current.filter((entry) => entry.id !== id),
|
|
490
|
+
{ id, target, getElement }
|
|
491
|
+
]);
|
|
492
|
+
},
|
|
493
|
+
[]
|
|
494
|
+
);
|
|
495
|
+
const unregisterTarget = (0, import_react2.useCallback)((id) => {
|
|
496
|
+
setRegisteredTargets((current) => current.filter((entry) => entry.id !== id));
|
|
497
|
+
}, []);
|
|
498
|
+
const visiblePageConfigs = pageConfigs.filter(
|
|
499
|
+
({ getElement }) => isElementVisibleForReporting(getElement())
|
|
500
|
+
);
|
|
501
|
+
const visibleRegisteredTargets = registeredTargets.filter(
|
|
502
|
+
({ getElement }) => isElementVisibleForReporting(getElement())
|
|
503
|
+
);
|
|
504
|
+
const createMode = visiblePageConfigs.length > 0 ? visiblePageConfigs[visiblePageConfigs.length - 1].createMode : defaultCreateMode;
|
|
505
|
+
const hasRegisteredTargets = visibleRegisteredTargets.length > 0;
|
|
364
506
|
const enterReportMode = (0, import_react2.useCallback)(() => {
|
|
365
507
|
setIsReportMode(true);
|
|
366
508
|
setIsPopoverOpen(false);
|
|
@@ -372,6 +514,42 @@ function IssueReportingProvider({
|
|
|
372
514
|
setModalState(initialModalState);
|
|
373
515
|
setIsPopoverOpen(true);
|
|
374
516
|
}, []);
|
|
517
|
+
const openPageIssueModal = (0, import_react2.useCallback)(() => {
|
|
518
|
+
if (!isEligible) {
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
openCreateModal(
|
|
522
|
+
buildGeneralPageTarget(mergedCopy, visibleRegisteredTargets, getPageUrl)
|
|
523
|
+
);
|
|
524
|
+
}, [
|
|
525
|
+
getPageUrl,
|
|
526
|
+
isEligible,
|
|
527
|
+
mergedCopy,
|
|
528
|
+
openCreateModal,
|
|
529
|
+
visibleRegisteredTargets
|
|
530
|
+
]);
|
|
531
|
+
const startNewIssue = (0, import_react2.useCallback)(() => {
|
|
532
|
+
if (!isEligible) {
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
if (createMode === "surface_required") {
|
|
536
|
+
if (hasRegisteredTargets) {
|
|
537
|
+
enterReportMode();
|
|
538
|
+
}
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
if (createMode === "surface_preferred" && hasRegisteredTargets) {
|
|
542
|
+
enterReportMode();
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
openPageIssueModal();
|
|
546
|
+
}, [
|
|
547
|
+
createMode,
|
|
548
|
+
enterReportMode,
|
|
549
|
+
hasRegisteredTargets,
|
|
550
|
+
isEligible,
|
|
551
|
+
openPageIssueModal
|
|
552
|
+
]);
|
|
375
553
|
const hydrateIssueIntoModal = (0, import_react2.useCallback)(
|
|
376
554
|
async (issueReportId, mode) => {
|
|
377
555
|
setModalState({
|
|
@@ -430,18 +608,9 @@ function IssueReportingProvider({
|
|
|
430
608
|
return;
|
|
431
609
|
}
|
|
432
610
|
const normalizedTarget = normalizeTarget(target, getPageUrl);
|
|
433
|
-
|
|
434
|
-
setModalState({
|
|
435
|
-
isOpen: true,
|
|
436
|
-
mode: "create",
|
|
437
|
-
issueReportId: null,
|
|
438
|
-
issue: null,
|
|
439
|
-
target: normalizedTarget,
|
|
440
|
-
error: null,
|
|
441
|
-
isHydrating: false
|
|
442
|
-
});
|
|
611
|
+
openCreateModal(normalizedTarget);
|
|
443
612
|
},
|
|
444
|
-
[getPageUrl, isEligible]
|
|
613
|
+
[getPageUrl, isEligible, openCreateModal]
|
|
445
614
|
);
|
|
446
615
|
const contextValue = (0, import_react2.useMemo)(
|
|
447
616
|
() => ({
|
|
@@ -451,9 +620,12 @@ function IssueReportingProvider({
|
|
|
451
620
|
principalId: principalId ?? null,
|
|
452
621
|
copy: mergedCopy,
|
|
453
622
|
isReportMode,
|
|
623
|
+
hasRegisteredTargets,
|
|
454
624
|
enterReportMode,
|
|
455
625
|
cancelReportMode,
|
|
456
626
|
selectPanel,
|
|
627
|
+
openPageIssueModal,
|
|
628
|
+
startNewIssue,
|
|
457
629
|
isPopoverOpen,
|
|
458
630
|
openPopover,
|
|
459
631
|
closePopover,
|
|
@@ -463,7 +635,8 @@ function IssueReportingProvider({
|
|
|
463
635
|
retryModalHydration,
|
|
464
636
|
scope,
|
|
465
637
|
setScope,
|
|
466
|
-
allowTenantScope
|
|
638
|
+
allowTenantScope,
|
|
639
|
+
createMode
|
|
467
640
|
}),
|
|
468
641
|
[
|
|
469
642
|
allowTenantScope,
|
|
@@ -471,30 +644,57 @@ function IssueReportingProvider({
|
|
|
471
644
|
client,
|
|
472
645
|
closeModal,
|
|
473
646
|
closePopover,
|
|
647
|
+
createMode,
|
|
474
648
|
enterReportMode,
|
|
649
|
+
hasRegisteredTargets,
|
|
475
650
|
isEligible,
|
|
476
651
|
isPopoverOpen,
|
|
477
652
|
isReportMode,
|
|
478
653
|
mergedCopy,
|
|
479
654
|
modalState,
|
|
480
655
|
openExistingIssueModal,
|
|
656
|
+
openPageIssueModal,
|
|
481
657
|
openPopover,
|
|
482
658
|
principalId,
|
|
483
659
|
reporterRoleHint,
|
|
484
660
|
retryModalHydration,
|
|
485
661
|
scope,
|
|
486
|
-
selectPanel
|
|
662
|
+
selectPanel,
|
|
663
|
+
startNewIssue
|
|
487
664
|
]
|
|
488
665
|
);
|
|
489
666
|
const reportModeValue = (0, import_react2.useMemo)(
|
|
490
667
|
() => ({
|
|
491
668
|
isReportMode,
|
|
492
669
|
selectPanel,
|
|
493
|
-
cancelReportMode
|
|
670
|
+
cancelReportMode,
|
|
671
|
+
hasRegisteredTargets,
|
|
672
|
+
registerTarget,
|
|
673
|
+
unregisterTarget
|
|
494
674
|
}),
|
|
495
|
-
[
|
|
675
|
+
[
|
|
676
|
+
cancelReportMode,
|
|
677
|
+
hasRegisteredTargets,
|
|
678
|
+
isReportMode,
|
|
679
|
+
registerTarget,
|
|
680
|
+
selectPanel,
|
|
681
|
+
unregisterTarget
|
|
682
|
+
]
|
|
683
|
+
);
|
|
684
|
+
const pageConfigRegistryValue = (0, import_react2.useMemo)(
|
|
685
|
+
() => ({
|
|
686
|
+
registerPageConfig,
|
|
687
|
+
unregisterPageConfig
|
|
688
|
+
}),
|
|
689
|
+
[registerPageConfig, unregisterPageConfig]
|
|
690
|
+
);
|
|
691
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
692
|
+
IssueReportingPageConfigRegistryContext.Provider,
|
|
693
|
+
{
|
|
694
|
+
value: pageConfigRegistryValue,
|
|
695
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ReportModeContext.Provider, { value: reportModeValue, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IssueReportingContext.Provider, { value: contextValue, children }) })
|
|
696
|
+
}
|
|
496
697
|
);
|
|
497
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ReportModeContext.Provider, { value: reportModeValue, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IssueReportingContext.Provider, { value: contextValue, children }) });
|
|
498
698
|
}
|
|
499
699
|
|
|
500
700
|
// src/components.tsx
|
|
@@ -660,10 +860,13 @@ function IssueReportPopover({ children }) {
|
|
|
660
860
|
openPopover,
|
|
661
861
|
closePopover,
|
|
662
862
|
enterReportMode,
|
|
863
|
+
openPageIssueModal,
|
|
663
864
|
openExistingIssueModal,
|
|
664
865
|
scope,
|
|
665
866
|
setScope,
|
|
666
|
-
allowTenantScope
|
|
867
|
+
allowTenantScope,
|
|
868
|
+
createMode,
|
|
869
|
+
hasRegisteredTargets
|
|
667
870
|
} = useIssueReporting();
|
|
668
871
|
const history = useIssueReportingHistory("all");
|
|
669
872
|
const status = useIssueReportingStatus();
|
|
@@ -677,6 +880,9 @@ function IssueReportPopover({ children }) {
|
|
|
677
880
|
[allItems]
|
|
678
881
|
);
|
|
679
882
|
const statusSummary = status.data ? `${status.data.open_count} open \xB7 ${status.data.recent_resolved_count} recently resolved` : "Status reflects the active scope.";
|
|
883
|
+
const showSectionFirst = createMode === "surface_required" || createMode === "surface_preferred" && hasRegisteredTargets;
|
|
884
|
+
const sectionActionDisabled = !hasRegisteredTargets;
|
|
885
|
+
const helperText = createMode === "surface_required" ? hasRegisteredTargets ? copy.surfaceRequiredDescription : copy.specificSectionUnavailableDescription : createMode === "surface_preferred" && !hasRegisteredTargets ? copy.specificSectionUnavailableDescription : null;
|
|
680
886
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
681
887
|
Popover.Root,
|
|
682
888
|
{
|
|
@@ -697,19 +903,66 @@ function IssueReportPopover({ children }) {
|
|
|
697
903
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { className: "text-sm font-semibold text-slate-900", children: copy.popoverTitle }),
|
|
698
904
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "mt-1 text-xs text-slate-500", children: statusSummary })
|
|
699
905
|
] }),
|
|
700
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
906
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex flex-wrap justify-end gap-2", children: showSectionFirst ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
907
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
908
|
+
"button",
|
|
909
|
+
{
|
|
910
|
+
type: "button",
|
|
911
|
+
className: cn(
|
|
912
|
+
"rounded-full px-3 py-2 text-xs font-semibold transition",
|
|
913
|
+
sectionActionDisabled ? "cursor-not-allowed bg-slate-200 text-slate-500" : "bg-slate-900 text-white hover:bg-slate-800"
|
|
914
|
+
),
|
|
915
|
+
onClick: () => {
|
|
916
|
+
if (sectionActionDisabled) {
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
closePopover();
|
|
920
|
+
enterReportMode();
|
|
921
|
+
},
|
|
922
|
+
disabled: sectionActionDisabled,
|
|
923
|
+
children: createMode === "surface_required" ? copy.reportNewAction : copy.reportSpecificAction
|
|
924
|
+
}
|
|
925
|
+
),
|
|
926
|
+
createMode !== "surface_required" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
927
|
+
"button",
|
|
928
|
+
{
|
|
929
|
+
type: "button",
|
|
930
|
+
className: "rounded-full border border-slate-200 px-3 py-2 text-xs font-semibold text-slate-700 transition hover:bg-slate-50",
|
|
931
|
+
onClick: () => {
|
|
932
|
+
closePopover();
|
|
933
|
+
openPageIssueModal();
|
|
934
|
+
},
|
|
935
|
+
children: copy.reportPageAction
|
|
936
|
+
}
|
|
937
|
+
) : null
|
|
938
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
939
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
940
|
+
"button",
|
|
941
|
+
{
|
|
942
|
+
type: "button",
|
|
943
|
+
className: "rounded-full bg-slate-900 px-3 py-2 text-xs font-semibold text-white transition hover:bg-slate-800",
|
|
944
|
+
onClick: () => {
|
|
945
|
+
closePopover();
|
|
946
|
+
openPageIssueModal();
|
|
947
|
+
},
|
|
948
|
+
children: createMode === "general_page" ? copy.reportPageAction : copy.reportNewAction
|
|
949
|
+
}
|
|
950
|
+
),
|
|
951
|
+
hasRegisteredTargets ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
952
|
+
"button",
|
|
953
|
+
{
|
|
954
|
+
type: "button",
|
|
955
|
+
className: "rounded-full border border-slate-200 px-3 py-2 text-xs font-semibold text-slate-700 transition hover:bg-slate-50",
|
|
956
|
+
onClick: () => {
|
|
957
|
+
closePopover();
|
|
958
|
+
enterReportMode();
|
|
959
|
+
},
|
|
960
|
+
children: copy.reportSpecificAction
|
|
961
|
+
}
|
|
962
|
+
) : null
|
|
963
|
+
] }) })
|
|
712
964
|
] }),
|
|
965
|
+
helperText ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "rounded-2xl border border-amber-200 bg-amber-50 px-4 py-3 text-xs text-amber-900", children: helperText }) : null,
|
|
713
966
|
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
967
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { children: status.error.message || copy.statusLoadFailed }),
|
|
715
968
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
@@ -986,9 +1239,23 @@ function ReportableSection({
|
|
|
986
1239
|
}) {
|
|
987
1240
|
const reportMode = useReportMode();
|
|
988
1241
|
const isSelectable = Boolean(reportMode?.isReportMode);
|
|
989
|
-
|
|
1242
|
+
const targetId = import_react4.default.useRef(/* @__PURE__ */ Symbol("reportable-section"));
|
|
1243
|
+
const elementRef = import_react4.default.useRef(null);
|
|
1244
|
+
(0, import_react4.useEffect)(() => {
|
|
1245
|
+
if (!reportMode) {
|
|
1246
|
+
return;
|
|
1247
|
+
}
|
|
1248
|
+
reportMode.registerTarget(targetId.current, reportableName, () => elementRef.current);
|
|
1249
|
+
return () => {
|
|
1250
|
+
reportMode.unregisterTarget(targetId.current);
|
|
1251
|
+
};
|
|
1252
|
+
}, [reportMode, reportableName]);
|
|
1253
|
+
return import_react4.default.createElement(
|
|
990
1254
|
Component,
|
|
991
1255
|
{
|
|
1256
|
+
ref: (node) => {
|
|
1257
|
+
elementRef.current = node;
|
|
1258
|
+
},
|
|
992
1259
|
className: cn(
|
|
993
1260
|
className,
|
|
994
1261
|
isSelectable && "cursor-pointer ring-2 ring-amber-400 transition hover:ring-amber-500"
|
|
@@ -1003,14 +1270,15 @@ function ReportableSection({
|
|
|
1003
1270
|
}
|
|
1004
1271
|
} : void 0,
|
|
1005
1272
|
role: isSelectable ? "button" : void 0,
|
|
1006
|
-
tabIndex: isSelectable ? 0 : void 0
|
|
1007
|
-
|
|
1008
|
-
|
|
1273
|
+
tabIndex: isSelectable ? 0 : void 0
|
|
1274
|
+
},
|
|
1275
|
+
children
|
|
1009
1276
|
);
|
|
1010
1277
|
}
|
|
1011
1278
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1012
1279
|
0 && (module.exports = {
|
|
1013
1280
|
FloatingIssueReportButton,
|
|
1281
|
+
IssueReportingPageConfig,
|
|
1014
1282
|
IssueReportingProvider,
|
|
1015
1283
|
ReportModeContext,
|
|
1016
1284
|
ReportableSection,
|
package/dist/index.mjs
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
X
|
|
10
10
|
} from "@phosphor-icons/react";
|
|
11
11
|
import { formatDistanceToNow } from "date-fns";
|
|
12
|
-
import { useEffect as useEffect2, useMemo as useMemo2, useState as useState2 } from "react";
|
|
12
|
+
import React2, { useEffect as useEffect2, useMemo as useMemo2, useState as useState2 } from "react";
|
|
13
13
|
|
|
14
14
|
// src/provider.tsx
|
|
15
15
|
import {
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
useContext as useContext2,
|
|
19
19
|
useEffect,
|
|
20
20
|
useMemo,
|
|
21
|
+
useRef,
|
|
21
22
|
useState
|
|
22
23
|
} from "react";
|
|
23
24
|
import {
|
|
@@ -52,10 +53,24 @@ var initialModalState = {
|
|
|
52
53
|
var IssueReportingContext = createContext2(
|
|
53
54
|
void 0
|
|
54
55
|
);
|
|
56
|
+
var IssueReportingPageConfigRegistryContext = createContext2(void 0);
|
|
57
|
+
var hiddenMarkerStyle = {
|
|
58
|
+
position: "absolute",
|
|
59
|
+
width: "1px",
|
|
60
|
+
height: "1px",
|
|
61
|
+
padding: 0,
|
|
62
|
+
margin: "-1px",
|
|
63
|
+
overflow: "hidden",
|
|
64
|
+
clip: "rect(0, 0, 0, 0)",
|
|
65
|
+
whiteSpace: "nowrap",
|
|
66
|
+
border: 0
|
|
67
|
+
};
|
|
55
68
|
var defaultIssueReportingCopy = {
|
|
56
69
|
entryAriaLabel: "Report issue",
|
|
57
70
|
popoverTitle: "Issue Reports",
|
|
58
71
|
reportNewAction: "Report New Issue",
|
|
72
|
+
reportPageAction: "Report This Page",
|
|
73
|
+
reportSpecificAction: "Pick Specific Section",
|
|
59
74
|
filtersAll: "All",
|
|
60
75
|
filtersUnresolved: "Unresolved",
|
|
61
76
|
filtersResolved: "Resolved",
|
|
@@ -72,6 +87,9 @@ var defaultIssueReportingCopy = {
|
|
|
72
87
|
reportModeTitle: "Report mode is active",
|
|
73
88
|
reportModeDescription: "Click a highlighted section to capture the broken surface.",
|
|
74
89
|
reportModeCancelAction: "Cancel",
|
|
90
|
+
generalPageTargetLabel: "Current Page",
|
|
91
|
+
surfaceRequiredDescription: "This page requires selecting a specific section before you submit a report.",
|
|
92
|
+
specificSectionUnavailableDescription: "No specific sections are registered on this page right now.",
|
|
75
93
|
createTitlePrefix: "Report Issue",
|
|
76
94
|
editTitlePrefix: "Edit Issue",
|
|
77
95
|
replyTitlePrefix: "Reply to",
|
|
@@ -136,6 +154,44 @@ function normalizeTarget(target, getPageUrl) {
|
|
|
136
154
|
metadata: target.metadata ?? {}
|
|
137
155
|
};
|
|
138
156
|
}
|
|
157
|
+
function summarizeTarget(target) {
|
|
158
|
+
if (typeof target === "string") {
|
|
159
|
+
const normalized = target.trim();
|
|
160
|
+
return {
|
|
161
|
+
component_key: normalized,
|
|
162
|
+
component_label: normalized,
|
|
163
|
+
surface_ref: null,
|
|
164
|
+
metadata: {}
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
const key = target.componentKey.trim();
|
|
168
|
+
return {
|
|
169
|
+
component_key: key,
|
|
170
|
+
component_label: (target.componentLabel ?? key).trim(),
|
|
171
|
+
surface_ref: target.surfaceRef ?? null,
|
|
172
|
+
metadata: target.metadata ?? {}
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function isElementVisibleForReporting(element) {
|
|
176
|
+
if (!element || typeof window === "undefined") {
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
let current = element;
|
|
180
|
+
while (current) {
|
|
181
|
+
if (current.getAttribute("aria-hidden") === "true") {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
if (current instanceof HTMLElement && (current.hidden || current.inert)) {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
const computedStyle = window.getComputedStyle(current);
|
|
188
|
+
if (computedStyle.display === "none" || computedStyle.visibility === "hidden" || computedStyle.visibility === "collapse") {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
current = current.parentElement;
|
|
192
|
+
}
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
139
195
|
function normalizeIssueTarget(issue) {
|
|
140
196
|
return {
|
|
141
197
|
component_key: issue.target.component_key,
|
|
@@ -217,6 +273,23 @@ function getIssueNoteLengthMessage(note, copy) {
|
|
|
217
273
|
}
|
|
218
274
|
return `${note.length}/${NOTE_MAX_LENGTH}`;
|
|
219
275
|
}
|
|
276
|
+
function buildGeneralPageTarget(copy, registeredTargets, getPageUrl) {
|
|
277
|
+
return {
|
|
278
|
+
component_key: "general_page",
|
|
279
|
+
component_label: copy.generalPageTargetLabel,
|
|
280
|
+
page_url: resolvePageUrl(getPageUrl),
|
|
281
|
+
surface_ref: null,
|
|
282
|
+
metadata: {
|
|
283
|
+
capture_mode: "general_page",
|
|
284
|
+
registered_target_count: registeredTargets.length,
|
|
285
|
+
...registeredTargets.length > 0 ? {
|
|
286
|
+
registered_targets: registeredTargets.map(
|
|
287
|
+
({ target }) => summarizeTarget(target)
|
|
288
|
+
)
|
|
289
|
+
} : {}
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
}
|
|
220
293
|
function useIssueReporting() {
|
|
221
294
|
const context = useContext2(IssueReportingContext);
|
|
222
295
|
if (!context) {
|
|
@@ -226,6 +299,25 @@ function useIssueReporting() {
|
|
|
226
299
|
}
|
|
227
300
|
return context;
|
|
228
301
|
}
|
|
302
|
+
function IssueReportingPageConfig({
|
|
303
|
+
createMode
|
|
304
|
+
}) {
|
|
305
|
+
const registry = useContext2(IssueReportingPageConfigRegistryContext);
|
|
306
|
+
const configId = useRef(/* @__PURE__ */ Symbol("issue-reporting-page-config"));
|
|
307
|
+
const markerRef = useRef(null);
|
|
308
|
+
if (!registry) {
|
|
309
|
+
throw new Error(
|
|
310
|
+
"IssueReportingPageConfig must be used within an IssueReportingProvider"
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
useEffect(() => {
|
|
314
|
+
registry.registerPageConfig(configId.current, createMode, () => markerRef.current);
|
|
315
|
+
return () => {
|
|
316
|
+
registry.unregisterPageConfig(configId.current);
|
|
317
|
+
};
|
|
318
|
+
}, [createMode, registry]);
|
|
319
|
+
return /* @__PURE__ */ jsx("span", { ref: markerRef, style: hiddenMarkerStyle });
|
|
320
|
+
}
|
|
229
321
|
function useIssueReportingStatus() {
|
|
230
322
|
const { client, isEligible, scope } = useIssueReporting();
|
|
231
323
|
return useQuery({
|
|
@@ -298,6 +390,7 @@ function IssueReportingProvider({
|
|
|
298
390
|
getPageUrl,
|
|
299
391
|
defaultScope,
|
|
300
392
|
allowTenantScope = false,
|
|
393
|
+
defaultCreateMode = "general_page",
|
|
301
394
|
copy,
|
|
302
395
|
children
|
|
303
396
|
}) {
|
|
@@ -315,6 +408,10 @@ function IssueReportingProvider({
|
|
|
315
408
|
const [scope, setScope] = useState(
|
|
316
409
|
() => resolveInitialScope(defaultScope, allowTenantScope)
|
|
317
410
|
);
|
|
411
|
+
const [pageConfigs, setPageConfigs] = useState([]);
|
|
412
|
+
const [registeredTargets, setRegisteredTargets] = useState(
|
|
413
|
+
[]
|
|
414
|
+
);
|
|
318
415
|
useEffect(() => {
|
|
319
416
|
setScope(resolveInitialScope(defaultScope, allowTenantScope));
|
|
320
417
|
}, [allowTenantScope, defaultScope]);
|
|
@@ -324,6 +421,51 @@ function IssueReportingProvider({
|
|
|
324
421
|
const openPopover = useCallback(() => {
|
|
325
422
|
setIsPopoverOpen(true);
|
|
326
423
|
}, []);
|
|
424
|
+
const openCreateModal = useCallback((target) => {
|
|
425
|
+
setIsPopoverOpen(false);
|
|
426
|
+
setIsReportMode(false);
|
|
427
|
+
setModalState({
|
|
428
|
+
isOpen: true,
|
|
429
|
+
mode: "create",
|
|
430
|
+
issueReportId: null,
|
|
431
|
+
issue: null,
|
|
432
|
+
target,
|
|
433
|
+
error: null,
|
|
434
|
+
isHydrating: false
|
|
435
|
+
});
|
|
436
|
+
}, []);
|
|
437
|
+
const registerPageConfig = useCallback(
|
|
438
|
+
(id, createMode2, getElement) => {
|
|
439
|
+
setPageConfigs((current) => [
|
|
440
|
+
...current.filter((entry) => entry.id !== id),
|
|
441
|
+
{ id, createMode: createMode2, getElement }
|
|
442
|
+
]);
|
|
443
|
+
},
|
|
444
|
+
[]
|
|
445
|
+
);
|
|
446
|
+
const unregisterPageConfig = useCallback((id) => {
|
|
447
|
+
setPageConfigs((current) => current.filter((entry) => entry.id !== id));
|
|
448
|
+
}, []);
|
|
449
|
+
const registerTarget = useCallback(
|
|
450
|
+
(id, target, getElement) => {
|
|
451
|
+
setRegisteredTargets((current) => [
|
|
452
|
+
...current.filter((entry) => entry.id !== id),
|
|
453
|
+
{ id, target, getElement }
|
|
454
|
+
]);
|
|
455
|
+
},
|
|
456
|
+
[]
|
|
457
|
+
);
|
|
458
|
+
const unregisterTarget = useCallback((id) => {
|
|
459
|
+
setRegisteredTargets((current) => current.filter((entry) => entry.id !== id));
|
|
460
|
+
}, []);
|
|
461
|
+
const visiblePageConfigs = pageConfigs.filter(
|
|
462
|
+
({ getElement }) => isElementVisibleForReporting(getElement())
|
|
463
|
+
);
|
|
464
|
+
const visibleRegisteredTargets = registeredTargets.filter(
|
|
465
|
+
({ getElement }) => isElementVisibleForReporting(getElement())
|
|
466
|
+
);
|
|
467
|
+
const createMode = visiblePageConfigs.length > 0 ? visiblePageConfigs[visiblePageConfigs.length - 1].createMode : defaultCreateMode;
|
|
468
|
+
const hasRegisteredTargets = visibleRegisteredTargets.length > 0;
|
|
327
469
|
const enterReportMode = useCallback(() => {
|
|
328
470
|
setIsReportMode(true);
|
|
329
471
|
setIsPopoverOpen(false);
|
|
@@ -335,6 +477,42 @@ function IssueReportingProvider({
|
|
|
335
477
|
setModalState(initialModalState);
|
|
336
478
|
setIsPopoverOpen(true);
|
|
337
479
|
}, []);
|
|
480
|
+
const openPageIssueModal = useCallback(() => {
|
|
481
|
+
if (!isEligible) {
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
openCreateModal(
|
|
485
|
+
buildGeneralPageTarget(mergedCopy, visibleRegisteredTargets, getPageUrl)
|
|
486
|
+
);
|
|
487
|
+
}, [
|
|
488
|
+
getPageUrl,
|
|
489
|
+
isEligible,
|
|
490
|
+
mergedCopy,
|
|
491
|
+
openCreateModal,
|
|
492
|
+
visibleRegisteredTargets
|
|
493
|
+
]);
|
|
494
|
+
const startNewIssue = useCallback(() => {
|
|
495
|
+
if (!isEligible) {
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
if (createMode === "surface_required") {
|
|
499
|
+
if (hasRegisteredTargets) {
|
|
500
|
+
enterReportMode();
|
|
501
|
+
}
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
if (createMode === "surface_preferred" && hasRegisteredTargets) {
|
|
505
|
+
enterReportMode();
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
openPageIssueModal();
|
|
509
|
+
}, [
|
|
510
|
+
createMode,
|
|
511
|
+
enterReportMode,
|
|
512
|
+
hasRegisteredTargets,
|
|
513
|
+
isEligible,
|
|
514
|
+
openPageIssueModal
|
|
515
|
+
]);
|
|
338
516
|
const hydrateIssueIntoModal = useCallback(
|
|
339
517
|
async (issueReportId, mode) => {
|
|
340
518
|
setModalState({
|
|
@@ -393,18 +571,9 @@ function IssueReportingProvider({
|
|
|
393
571
|
return;
|
|
394
572
|
}
|
|
395
573
|
const normalizedTarget = normalizeTarget(target, getPageUrl);
|
|
396
|
-
|
|
397
|
-
setModalState({
|
|
398
|
-
isOpen: true,
|
|
399
|
-
mode: "create",
|
|
400
|
-
issueReportId: null,
|
|
401
|
-
issue: null,
|
|
402
|
-
target: normalizedTarget,
|
|
403
|
-
error: null,
|
|
404
|
-
isHydrating: false
|
|
405
|
-
});
|
|
574
|
+
openCreateModal(normalizedTarget);
|
|
406
575
|
},
|
|
407
|
-
[getPageUrl, isEligible]
|
|
576
|
+
[getPageUrl, isEligible, openCreateModal]
|
|
408
577
|
);
|
|
409
578
|
const contextValue = useMemo(
|
|
410
579
|
() => ({
|
|
@@ -414,9 +583,12 @@ function IssueReportingProvider({
|
|
|
414
583
|
principalId: principalId ?? null,
|
|
415
584
|
copy: mergedCopy,
|
|
416
585
|
isReportMode,
|
|
586
|
+
hasRegisteredTargets,
|
|
417
587
|
enterReportMode,
|
|
418
588
|
cancelReportMode,
|
|
419
589
|
selectPanel,
|
|
590
|
+
openPageIssueModal,
|
|
591
|
+
startNewIssue,
|
|
420
592
|
isPopoverOpen,
|
|
421
593
|
openPopover,
|
|
422
594
|
closePopover,
|
|
@@ -426,7 +598,8 @@ function IssueReportingProvider({
|
|
|
426
598
|
retryModalHydration,
|
|
427
599
|
scope,
|
|
428
600
|
setScope,
|
|
429
|
-
allowTenantScope
|
|
601
|
+
allowTenantScope,
|
|
602
|
+
createMode
|
|
430
603
|
}),
|
|
431
604
|
[
|
|
432
605
|
allowTenantScope,
|
|
@@ -434,30 +607,57 @@ function IssueReportingProvider({
|
|
|
434
607
|
client,
|
|
435
608
|
closeModal,
|
|
436
609
|
closePopover,
|
|
610
|
+
createMode,
|
|
437
611
|
enterReportMode,
|
|
612
|
+
hasRegisteredTargets,
|
|
438
613
|
isEligible,
|
|
439
614
|
isPopoverOpen,
|
|
440
615
|
isReportMode,
|
|
441
616
|
mergedCopy,
|
|
442
617
|
modalState,
|
|
443
618
|
openExistingIssueModal,
|
|
619
|
+
openPageIssueModal,
|
|
444
620
|
openPopover,
|
|
445
621
|
principalId,
|
|
446
622
|
reporterRoleHint,
|
|
447
623
|
retryModalHydration,
|
|
448
624
|
scope,
|
|
449
|
-
selectPanel
|
|
625
|
+
selectPanel,
|
|
626
|
+
startNewIssue
|
|
450
627
|
]
|
|
451
628
|
);
|
|
452
629
|
const reportModeValue = useMemo(
|
|
453
630
|
() => ({
|
|
454
631
|
isReportMode,
|
|
455
632
|
selectPanel,
|
|
456
|
-
cancelReportMode
|
|
633
|
+
cancelReportMode,
|
|
634
|
+
hasRegisteredTargets,
|
|
635
|
+
registerTarget,
|
|
636
|
+
unregisterTarget
|
|
457
637
|
}),
|
|
458
|
-
[
|
|
638
|
+
[
|
|
639
|
+
cancelReportMode,
|
|
640
|
+
hasRegisteredTargets,
|
|
641
|
+
isReportMode,
|
|
642
|
+
registerTarget,
|
|
643
|
+
selectPanel,
|
|
644
|
+
unregisterTarget
|
|
645
|
+
]
|
|
646
|
+
);
|
|
647
|
+
const pageConfigRegistryValue = useMemo(
|
|
648
|
+
() => ({
|
|
649
|
+
registerPageConfig,
|
|
650
|
+
unregisterPageConfig
|
|
651
|
+
}),
|
|
652
|
+
[registerPageConfig, unregisterPageConfig]
|
|
653
|
+
);
|
|
654
|
+
return /* @__PURE__ */ jsx(
|
|
655
|
+
IssueReportingPageConfigRegistryContext.Provider,
|
|
656
|
+
{
|
|
657
|
+
value: pageConfigRegistryValue,
|
|
658
|
+
children: /* @__PURE__ */ jsx(ReportModeContext.Provider, { value: reportModeValue, children: /* @__PURE__ */ jsx(IssueReportingContext.Provider, { value: contextValue, children }) })
|
|
659
|
+
}
|
|
459
660
|
);
|
|
460
|
-
return /* @__PURE__ */ jsx(ReportModeContext.Provider, { value: reportModeValue, children: /* @__PURE__ */ jsx(IssueReportingContext.Provider, { value: contextValue, children }) });
|
|
461
661
|
}
|
|
462
662
|
|
|
463
663
|
// src/components.tsx
|
|
@@ -623,10 +823,13 @@ function IssueReportPopover({ children }) {
|
|
|
623
823
|
openPopover,
|
|
624
824
|
closePopover,
|
|
625
825
|
enterReportMode,
|
|
826
|
+
openPageIssueModal,
|
|
626
827
|
openExistingIssueModal,
|
|
627
828
|
scope,
|
|
628
829
|
setScope,
|
|
629
|
-
allowTenantScope
|
|
830
|
+
allowTenantScope,
|
|
831
|
+
createMode,
|
|
832
|
+
hasRegisteredTargets
|
|
630
833
|
} = useIssueReporting();
|
|
631
834
|
const history = useIssueReportingHistory("all");
|
|
632
835
|
const status = useIssueReportingStatus();
|
|
@@ -640,6 +843,9 @@ function IssueReportPopover({ children }) {
|
|
|
640
843
|
[allItems]
|
|
641
844
|
);
|
|
642
845
|
const statusSummary = status.data ? `${status.data.open_count} open \xB7 ${status.data.recent_resolved_count} recently resolved` : "Status reflects the active scope.";
|
|
846
|
+
const showSectionFirst = createMode === "surface_required" || createMode === "surface_preferred" && hasRegisteredTargets;
|
|
847
|
+
const sectionActionDisabled = !hasRegisteredTargets;
|
|
848
|
+
const helperText = createMode === "surface_required" ? hasRegisteredTargets ? copy.surfaceRequiredDescription : copy.specificSectionUnavailableDescription : createMode === "surface_preferred" && !hasRegisteredTargets ? copy.specificSectionUnavailableDescription : null;
|
|
643
849
|
return /* @__PURE__ */ jsxs(
|
|
644
850
|
Popover.Root,
|
|
645
851
|
{
|
|
@@ -660,19 +866,66 @@ function IssueReportPopover({ children }) {
|
|
|
660
866
|
/* @__PURE__ */ jsx2("h3", { className: "text-sm font-semibold text-slate-900", children: copy.popoverTitle }),
|
|
661
867
|
/* @__PURE__ */ jsx2("p", { className: "mt-1 text-xs text-slate-500", children: statusSummary })
|
|
662
868
|
] }),
|
|
663
|
-
/* @__PURE__ */ jsx2(
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
869
|
+
/* @__PURE__ */ jsx2("div", { className: "flex flex-wrap justify-end gap-2", children: showSectionFirst ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
870
|
+
/* @__PURE__ */ jsx2(
|
|
871
|
+
"button",
|
|
872
|
+
{
|
|
873
|
+
type: "button",
|
|
874
|
+
className: cn(
|
|
875
|
+
"rounded-full px-3 py-2 text-xs font-semibold transition",
|
|
876
|
+
sectionActionDisabled ? "cursor-not-allowed bg-slate-200 text-slate-500" : "bg-slate-900 text-white hover:bg-slate-800"
|
|
877
|
+
),
|
|
878
|
+
onClick: () => {
|
|
879
|
+
if (sectionActionDisabled) {
|
|
880
|
+
return;
|
|
881
|
+
}
|
|
882
|
+
closePopover();
|
|
883
|
+
enterReportMode();
|
|
884
|
+
},
|
|
885
|
+
disabled: sectionActionDisabled,
|
|
886
|
+
children: createMode === "surface_required" ? copy.reportNewAction : copy.reportSpecificAction
|
|
887
|
+
}
|
|
888
|
+
),
|
|
889
|
+
createMode !== "surface_required" ? /* @__PURE__ */ jsx2(
|
|
890
|
+
"button",
|
|
891
|
+
{
|
|
892
|
+
type: "button",
|
|
893
|
+
className: "rounded-full border border-slate-200 px-3 py-2 text-xs font-semibold text-slate-700 transition hover:bg-slate-50",
|
|
894
|
+
onClick: () => {
|
|
895
|
+
closePopover();
|
|
896
|
+
openPageIssueModal();
|
|
897
|
+
},
|
|
898
|
+
children: copy.reportPageAction
|
|
899
|
+
}
|
|
900
|
+
) : null
|
|
901
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
902
|
+
/* @__PURE__ */ jsx2(
|
|
903
|
+
"button",
|
|
904
|
+
{
|
|
905
|
+
type: "button",
|
|
906
|
+
className: "rounded-full bg-slate-900 px-3 py-2 text-xs font-semibold text-white transition hover:bg-slate-800",
|
|
907
|
+
onClick: () => {
|
|
908
|
+
closePopover();
|
|
909
|
+
openPageIssueModal();
|
|
910
|
+
},
|
|
911
|
+
children: createMode === "general_page" ? copy.reportPageAction : copy.reportNewAction
|
|
912
|
+
}
|
|
913
|
+
),
|
|
914
|
+
hasRegisteredTargets ? /* @__PURE__ */ jsx2(
|
|
915
|
+
"button",
|
|
916
|
+
{
|
|
917
|
+
type: "button",
|
|
918
|
+
className: "rounded-full border border-slate-200 px-3 py-2 text-xs font-semibold text-slate-700 transition hover:bg-slate-50",
|
|
919
|
+
onClick: () => {
|
|
920
|
+
closePopover();
|
|
921
|
+
enterReportMode();
|
|
922
|
+
},
|
|
923
|
+
children: copy.reportSpecificAction
|
|
924
|
+
}
|
|
925
|
+
) : null
|
|
926
|
+
] }) })
|
|
675
927
|
] }),
|
|
928
|
+
helperText ? /* @__PURE__ */ jsx2("div", { className: "rounded-2xl border border-amber-200 bg-amber-50 px-4 py-3 text-xs text-amber-900", children: helperText }) : null,
|
|
676
929
|
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
930
|
/* @__PURE__ */ jsx2("div", { children: status.error.message || copy.statusLoadFailed }),
|
|
678
931
|
/* @__PURE__ */ jsx2(
|
|
@@ -949,9 +1202,23 @@ function ReportableSection({
|
|
|
949
1202
|
}) {
|
|
950
1203
|
const reportMode = useReportMode();
|
|
951
1204
|
const isSelectable = Boolean(reportMode?.isReportMode);
|
|
952
|
-
|
|
1205
|
+
const targetId = React2.useRef(/* @__PURE__ */ Symbol("reportable-section"));
|
|
1206
|
+
const elementRef = React2.useRef(null);
|
|
1207
|
+
useEffect2(() => {
|
|
1208
|
+
if (!reportMode) {
|
|
1209
|
+
return;
|
|
1210
|
+
}
|
|
1211
|
+
reportMode.registerTarget(targetId.current, reportableName, () => elementRef.current);
|
|
1212
|
+
return () => {
|
|
1213
|
+
reportMode.unregisterTarget(targetId.current);
|
|
1214
|
+
};
|
|
1215
|
+
}, [reportMode, reportableName]);
|
|
1216
|
+
return React2.createElement(
|
|
953
1217
|
Component,
|
|
954
1218
|
{
|
|
1219
|
+
ref: (node) => {
|
|
1220
|
+
elementRef.current = node;
|
|
1221
|
+
},
|
|
955
1222
|
className: cn(
|
|
956
1223
|
className,
|
|
957
1224
|
isSelectable && "cursor-pointer ring-2 ring-amber-400 transition hover:ring-amber-500"
|
|
@@ -966,13 +1233,14 @@ function ReportableSection({
|
|
|
966
1233
|
}
|
|
967
1234
|
} : void 0,
|
|
968
1235
|
role: isSelectable ? "button" : void 0,
|
|
969
|
-
tabIndex: isSelectable ? 0 : void 0
|
|
970
|
-
|
|
971
|
-
|
|
1236
|
+
tabIndex: isSelectable ? 0 : void 0
|
|
1237
|
+
},
|
|
1238
|
+
children
|
|
972
1239
|
);
|
|
973
1240
|
}
|
|
974
1241
|
export {
|
|
975
1242
|
FloatingIssueReportButton,
|
|
1243
|
+
IssueReportingPageConfig,
|
|
976
1244
|
IssueReportingProvider,
|
|
977
1245
|
ReportModeContext,
|
|
978
1246
|
ReportableSection,
|
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.3",
|
|
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",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"support"
|
|
25
25
|
],
|
|
26
26
|
"author": "buildooor",
|
|
27
|
-
"license": "
|
|
27
|
+
"license": "MIT",
|
|
28
28
|
"repository": {
|
|
29
29
|
"type": "git",
|
|
30
30
|
"url": "https://github.com/build000r/sweet-potato"
|
|
@@ -72,4 +72,4 @@
|
|
|
72
72
|
"engines": {
|
|
73
73
|
"node": ">=18.0.0"
|
|
74
74
|
}
|
|
75
|
-
}
|
|
75
|
+
}
|