anton-bakker-feedback 0.4.3 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +171 -0
- package/dist/chunks/{i18n-Cba0xHU-.js → i18n-Cn2yFMbd.js} +4 -2
- package/dist/chunks/i18n-Cn2yFMbd.js.map +1 -0
- package/dist/chunks/ingest-transport-DAA_pJNb.js +245 -0
- package/dist/chunks/ingest-transport-DAA_pJNb.js.map +1 -0
- package/dist/chunks/normalisers-Cbd-i4Jh.js +97 -0
- package/dist/chunks/normalisers-Cbd-i4Jh.js.map +1 -0
- package/dist/chunks/{observability-CfwHKObh.js → observability-DUK725zv.js} +3 -3
- package/dist/chunks/{observability-CfwHKObh.js.map → observability-DUK725zv.js.map} +1 -1
- package/dist/chunks/{submitResolver-DsfkrUHY.js → submitResolver-wfr4kbEM.js} +60 -39
- package/dist/chunks/submitResolver-wfr4kbEM.js.map +1 -0
- package/dist/core/config.d.ts +11 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/ingest-transport.d.ts +97 -0
- package/dist/core/ingest-transport.d.ts.map +1 -1
- package/dist/core/normalisers.d.ts +29 -0
- package/dist/core/normalisers.d.ts.map +1 -0
- package/dist/core/observability.d.ts.map +1 -1
- package/dist/core/request-headers.d.ts +22 -0
- package/dist/core/request-headers.d.ts.map +1 -0
- package/dist/core/state-metrics.d.ts +19 -0
- package/dist/core/state-metrics.d.ts.map +1 -0
- package/dist/core/transforms.d.ts.map +1 -1
- package/dist/core/types.d.ts +93 -3
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.js +17 -99
- package/dist/index.js.map +1 -1
- package/dist/react/FeedbackConfirm.d.ts.map +1 -1
- package/dist/react/FeedbackDialogue.d.ts.map +1 -1
- package/dist/react/admin/ActivityTimeline.d.ts +9 -0
- package/dist/react/admin/ActivityTimeline.d.ts.map +1 -0
- package/dist/react/admin/AnnotationComposer.d.ts +30 -0
- package/dist/react/admin/AnnotationComposer.d.ts.map +1 -0
- package/dist/react/admin/FeedbackListPage.d.ts.map +1 -1
- package/dist/react/admin/FeedbackUpdatePage.d.ts +6 -0
- package/dist/react/admin/FeedbackUpdatePage.d.ts.map +1 -1
- package/dist/react/admin/LifecycleControls.d.ts +28 -0
- package/dist/react/admin/LifecycleControls.d.ts.map +1 -0
- package/dist/react/admin/__tests__/ActivityTimeline.test.d.ts +2 -0
- package/dist/react/admin/__tests__/ActivityTimeline.test.d.ts.map +1 -0
- package/dist/react/admin/__tests__/AnnotationComposer.test.d.ts +2 -0
- package/dist/react/admin/__tests__/AnnotationComposer.test.d.ts.map +1 -0
- package/dist/react/admin/__tests__/LifecycleControls.test.d.ts +2 -0
- package/dist/react/admin/__tests__/LifecycleControls.test.d.ts.map +1 -0
- package/dist/react/admin/hooks/can.d.ts +47 -0
- package/dist/react/admin/hooks/can.d.ts.map +1 -0
- package/dist/react/admin/hooks/useStatusTransition.d.ts +16 -2
- package/dist/react/admin/hooks/useStatusTransition.d.ts.map +1 -1
- package/dist/react/admin/index.d.ts +14 -1
- package/dist/react/admin/index.d.ts.map +1 -1
- package/dist/react/admin/index.js +312 -29
- package/dist/react/admin/index.js.map +1 -1
- package/dist/react/admin/types.d.ts +13 -0
- package/dist/react/admin/types.d.ts.map +1 -1
- package/dist/react/resolver/submitResolver.d.ts +10 -7
- package/dist/react/resolver/submitResolver.d.ts.map +1 -1
- package/dist/react.js +26 -9
- package/dist/react.js.map +1 -1
- package/dist/server/index.d.ts +4 -5
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/listHandler.d.ts +15 -8
- package/dist/server/listHandler.d.ts.map +1 -1
- package/dist/server.js +22 -170
- package/dist/server.js.map +1 -1
- package/package.json +13 -1
- package/dist/chunks/i18n-Cba0xHU-.js.map +0 -1
- package/dist/chunks/submitResolver-DsfkrUHY.js.map +0 -1
- package/dist/server/FeedbackService.d.ts +0 -46
- package/dist/server/FeedbackService.d.ts.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FeedbackListPage.d.ts","sourceRoot":"","sources":["../../../src/react/admin/FeedbackListPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,KAAK,EAAsB,cAAc,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAC5F,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"FeedbackListPage.d.ts","sourceRoot":"","sources":["../../../src/react/admin/FeedbackListPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,KAAK,EAAsB,cAAc,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAC5F,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAwBxD,iBAAS,WAAW,CAAC,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,EAAE,cAAc,CAAA;CAAE,qBAE1D;AAED,iBAAS,SAAS,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,qBAElD;AAED,QAAA,MAAM,gBAAgB,8FA0FrB,CAAC;AAGF,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC"}
|
|
@@ -3,6 +3,12 @@ import { FeedbackUpdatePageProps } from './types.js';
|
|
|
3
3
|
* FeedbackUpdatePage.tsx
|
|
4
4
|
* Location: src/react/admin/FeedbackUpdatePage.tsx
|
|
5
5
|
*
|
|
6
|
+
* @deprecated This component is deprecated and will be removed in a future version.
|
|
7
|
+
* Use FeedbackAnnotateClient.appendActivity() and FeedbackAnnotateClient.transitionStatus()
|
|
8
|
+
* from ingest-transport.ts instead. The whole-array corrections[] PATCH has been removed
|
|
9
|
+
* to prevent data loss (sagrosoft20-class silent drop). This shell is kept for backward
|
|
10
|
+
* compatibility during stage-3 consumer migration.
|
|
11
|
+
*
|
|
6
12
|
* Admin update page for editing mutable fields of a feedback item.
|
|
7
13
|
* Uses UpdatePage + EnumField + TextField + TextareaField from anton-bakker-tailwind-components.
|
|
8
14
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FeedbackUpdatePage.d.ts","sourceRoot":"","sources":["../../../src/react/admin/FeedbackUpdatePage.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"FeedbackUpdatePage.d.ts","sourceRoot":"","sources":["../../../src/react/admin/FeedbackUpdatePage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAiC1D,QAAA,MAAM,kBAAkB,gGA6LvB,CAAC;AAGF,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { StoredFeedbackItem } from '../../core/types.js';
|
|
2
|
+
import { FeedbackAnnotateClient } from '../../core/ingest-transport.js';
|
|
3
|
+
import { CapabilityKey } from './hooks/can.js';
|
|
4
|
+
/**
|
|
5
|
+
* LifecycleControls.tsx
|
|
6
|
+
* Location: src/react/admin/LifecycleControls.tsx
|
|
7
|
+
*
|
|
8
|
+
* Legal+authorized transition buttons, assignment, duplicate merge via transitionStatus.
|
|
9
|
+
* Surfaces AnnotateResult.conflictReason inline.
|
|
10
|
+
* Renders only transition buttons for targets where can(requiredCapability) is true.
|
|
11
|
+
*/
|
|
12
|
+
import * as React from 'react';
|
|
13
|
+
export interface LifecycleControlsProps {
|
|
14
|
+
feedbackId: string;
|
|
15
|
+
item: StoredFeedbackItem;
|
|
16
|
+
currentUser: {
|
|
17
|
+
userId: string;
|
|
18
|
+
name?: string;
|
|
19
|
+
};
|
|
20
|
+
can: (cap: CapabilityKey) => boolean;
|
|
21
|
+
transitionStatus: FeedbackAnnotateClient['transitionStatus'];
|
|
22
|
+
onTransitioned?: () => void;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* LifecycleControls renders only transition buttons for targets where can(requiredCapability) is true.
|
|
26
|
+
*/
|
|
27
|
+
export declare const LifecycleControls: React.ForwardRefExoticComponent<LifecycleControlsProps & React.RefAttributes<HTMLDivElement>>;
|
|
28
|
+
//# sourceMappingURL=LifecycleControls.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LifecycleControls.d.ts","sourceRoot":"","sources":["../../../src/react/admin/LifecycleControls.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,kBAAkB,EAAkB,MAAM,qBAAqB,CAAC;AAC9E,OAAO,KAAK,EAAE,sBAAsB,EAA2B,MAAM,gCAAgC,CAAC;AACtG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAGpD,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,kBAAkB,CAAC;IACzB,WAAW,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,GAAG,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC;IACrC,gBAAgB,EAAE,sBAAsB,CAAC,kBAAkB,CAAC,CAAC;IAC7D,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;CAC7B;AA8BD;;GAEG;AACH,eAAO,MAAM,iBAAiB,+FA6H7B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ActivityTimeline.test.d.ts","sourceRoot":"","sources":["../../../../src/react/admin/__tests__/ActivityTimeline.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AnnotationComposer.test.d.ts","sourceRoot":"","sources":["../../../../src/react/admin/__tests__/AnnotationComposer.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LifecycleControls.test.d.ts","sourceRoot":"","sources":["../../../../src/react/admin/__tests__/LifecycleControls.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { StoredFeedbackItem } from '../../../core/types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Capability keys for feedback operations.
|
|
5
|
+
* Each key grants permission for a specific action on feedback items.
|
|
6
|
+
*/
|
|
7
|
+
export type CapabilityKey = 'feedback:comment' | 'feedback:annotate' | 'feedback:triage' | 'feedback:assign' | 'feedback:resolve' | 'feedback:verify' | 'feedback:reopen' | 'feedback:reject' | 'feedback:delete';
|
|
8
|
+
/**
|
|
9
|
+
* Record mapping each capability key to itself.
|
|
10
|
+
* Facilitates autocomplete and exhaustiveness checking.
|
|
11
|
+
*/
|
|
12
|
+
export declare const CAPABILITIES: Record<CapabilityKey, CapabilityKey>;
|
|
13
|
+
/**
|
|
14
|
+
* User identity with resolved capabilities.
|
|
15
|
+
* Consuming apps (PEP) translate their identity system to this format at the system boundary.
|
|
16
|
+
*/
|
|
17
|
+
export interface CapabilityUser {
|
|
18
|
+
userId: string;
|
|
19
|
+
capabilities: Set<CapabilityKey>;
|
|
20
|
+
}
|
|
21
|
+
/** Alias for CapabilityUser — both names refer to the same type. */
|
|
22
|
+
export type UserWithCapabilities = CapabilityUser;
|
|
23
|
+
/**
|
|
24
|
+
* Pure capability-based authorization guard.
|
|
25
|
+
* Returns true if the user has the specified capability OR if ownership rules grant it.
|
|
26
|
+
*
|
|
27
|
+
* Ownership rules:
|
|
28
|
+
* - Submitters (userId === item.submittedBy) can comment and annotate without explicit capability
|
|
29
|
+
* - Reporters (userId in item.reporters[].userId) can comment and annotate without explicit capability
|
|
30
|
+
* - All other capabilities require explicit presence in user.capabilities
|
|
31
|
+
*
|
|
32
|
+
* @param capability - The capability being checked
|
|
33
|
+
* @param item - The feedback item
|
|
34
|
+
* @param user - The user with resolved capabilities
|
|
35
|
+
* @returns true if authorized, false otherwise
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* const user = { userId: 'alice', capabilities: new Set(['feedback:triage']) };
|
|
39
|
+
* can('feedback:triage', item, user) // → true (explicit capability)
|
|
40
|
+
* can('feedback:comment', item, user) // → false (no capability, not owner)
|
|
41
|
+
*
|
|
42
|
+
* const submitter = { userId: item.submittedBy, capabilities: new Set([]) };
|
|
43
|
+
* can('feedback:comment', item, submitter) // → true (ownership rule)
|
|
44
|
+
* can('feedback:triage', item, submitter) // → false (ownership doesn't grant admin capabilities)
|
|
45
|
+
*/
|
|
46
|
+
export declare function can(capability: CapabilityKey, item: StoredFeedbackItem, user: CapabilityUser): boolean;
|
|
47
|
+
//# sourceMappingURL=can.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"can.d.ts","sourceRoot":"","sources":["../../../../src/react/admin/hooks/can.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAEjE;;;GAGG;AACH,MAAM,MAAM,aAAa,GACrB,kBAAkB,GAClB,mBAAmB,GACnB,iBAAiB,GACjB,iBAAiB,GACjB,kBAAkB,GAClB,iBAAiB,GACjB,iBAAiB,GACjB,iBAAiB,GACjB,iBAAiB,CAAC;AAEtB;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,aAAa,EAAE,aAAa,CAU7D,CAAC;AAEF;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,GAAG,CAAC,aAAa,CAAC,CAAC;CAClC;AAED,oEAAoE;AACpE,MAAM,MAAM,oBAAoB,GAAG,cAAc,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,GAAG,CACjB,UAAU,EAAE,aAAa,EACzB,IAAI,EAAE,kBAAkB,EACxB,IAAI,EAAE,cAAc,GACnB,OAAO,CAiBT"}
|
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
import { FeedbackStatus } from '../../../core/types.js';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Exhaustive legal transitions for the 12-state QA lifecycle. Readonly so consumers and
|
|
5
|
+
* tests can enumerate legal pairs. No self-loops.
|
|
6
|
+
*/
|
|
7
|
+
export declare const QA_LIFECYCLE_TRANSITIONS: Readonly<Record<FeedbackStatus, readonly FeedbackStatus[]>>;
|
|
8
|
+
/**
|
|
9
|
+
* Pure predicate: is a transition from `from` to `to` legal in the QA lifecycle?
|
|
10
|
+
* Self-transitions are never legal.
|
|
11
|
+
*
|
|
12
|
+
* @param from - current status
|
|
13
|
+
* @param to - proposed target status
|
|
14
|
+
* @returns true if the transition is encoded in QA_LIFECYCLE_TRANSITIONS
|
|
15
|
+
*/
|
|
16
|
+
export declare function canTransitionTo(from: FeedbackStatus, to: FeedbackStatus): boolean;
|
|
3
17
|
export interface UseStatusTransitionResult {
|
|
4
18
|
/** Statuses reachable from the current status (current status excluded) */
|
|
5
19
|
allowedTargets: FeedbackStatus[];
|
|
@@ -11,8 +25,8 @@ export interface UseStatusTransitionResult {
|
|
|
11
25
|
* @returns Object with `allowedTargets` — statuses the item can transition to
|
|
12
26
|
*
|
|
13
27
|
* @example
|
|
14
|
-
* const { allowedTargets } = useStatusTransition('
|
|
15
|
-
* // allowedTargets → ['
|
|
28
|
+
* const { allowedTargets } = useStatusTransition('new');
|
|
29
|
+
* // allowedTargets → ['triaged']
|
|
16
30
|
*/
|
|
17
31
|
export declare function useStatusTransition(currentStatus: FeedbackStatus): UseStatusTransitionResult;
|
|
18
32
|
//# sourceMappingURL=useStatusTransition.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useStatusTransition.d.ts","sourceRoot":"","sources":["../../../../src/react/admin/hooks/useStatusTransition.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"useStatusTransition.d.ts","sourceRoot":"","sources":["../../../../src/react/admin/hooks/useStatusTransition.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D;;;GAGG;AACH,eAAO,MAAM,wBAAwB,EAAE,QAAQ,CAAC,MAAM,CAAC,cAAc,EAAE,SAAS,cAAc,EAAE,CAAC,CAahG,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,cAAc,GAAG,OAAO,CAGjF;AAED,MAAM,WAAW,yBAAyB;IACxC,2EAA2E;IAC3E,cAAc,EAAE,cAAc,EAAE,CAAC;CAClC;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,aAAa,EAAE,cAAc,GAAG,yBAAyB,CAG5F"}
|
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
export { FeedbackListPage, StatusBadge, TypeBadge } from './FeedbackListPage.js';
|
|
2
2
|
export { FeedbackDetailPage } from './FeedbackDetailPage.js';
|
|
3
|
+
/**
|
|
4
|
+
* @deprecated FeedbackUpdatePage is superseded by the enriched FeedbackDetailPage with appendActivity + transitionStatus.
|
|
5
|
+
* Use createIngestAnnotateClient + FeedbackDetailPage with the new IoC props instead.
|
|
6
|
+
* This export is kept for backward compatibility and will be removed in a future major version.
|
|
7
|
+
*/
|
|
3
8
|
export { FeedbackUpdatePage } from './FeedbackUpdatePage.js';
|
|
9
|
+
export { ActivityTimeline } from './ActivityTimeline.js';
|
|
10
|
+
export { AnnotationComposer } from './AnnotationComposer.js';
|
|
11
|
+
export { LifecycleControls } from './LifecycleControls.js';
|
|
4
12
|
export { useStatusTransition } from './hooks/useStatusTransition.js';
|
|
13
|
+
export { can, CAPABILITIES } from './hooks/can.js';
|
|
14
|
+
export type { CapabilityKey, CapabilityUser, UserWithCapabilities } from './hooks/can.js';
|
|
5
15
|
export type { AdminPageProps, FeedbackListPageProps, FeedbackDetailPageProps, FeedbackUpdatePageProps, } from './types.js';
|
|
6
|
-
export type { StoredFeedbackItem, FeedbackStatus, FeedbackType, FeedbackScope, FeedbackSize, FeedbackEnvironment, Reporter, DialogueEntry, CorrectionEntry, FeedbackContext, } from '../../core/types.js';
|
|
16
|
+
export type { StoredFeedbackItem, FeedbackStatus, FeedbackType, FeedbackScope, FeedbackSize, FeedbackEnvironment, Reporter, DialogueEntry, CorrectionEntry, FeedbackContext, ActivityEntry, ActivityKind, ActivityActor, } from '../../core/types.js';
|
|
17
|
+
export type { FeedbackAnnotateClient, AnnotateResult, TransitionStatusOptions, } from '../../core/ingest-transport.js';
|
|
18
|
+
export { createIngestAnnotateClient } from '../../core/ingest-transport.js';
|
|
19
|
+
export { normaliseStoredItem } from '../../core/normalisers.js';
|
|
7
20
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react/admin/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACjF,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAG7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAGrE,YAAY,EACV,cAAc,EACd,qBAAqB,EACrB,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,YAAY,CAAC;AAGpB,YAAY,EACV,kBAAkB,EAClB,cAAc,EACd,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,mBAAmB,EACnB,QAAQ,EACR,aAAa,EACb,eAAe,EACf,eAAe,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react/admin/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACjF,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D;;;;GAIG;AACH,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAG7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAG3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAGrE,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAG1F,YAAY,EACV,cAAc,EACd,qBAAqB,EACrB,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,YAAY,CAAC;AAGpB,YAAY,EACV,kBAAkB,EAClB,cAAc,EACd,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,mBAAmB,EACnB,QAAQ,EACR,aAAa,EACb,eAAe,EACf,eAAe,EACf,aAAa,EACb,YAAY,EACZ,aAAa,GACd,MAAM,qBAAqB,CAAC;AAG7B,YAAY,EACV,sBAAsB,EACtB,cAAc,EACd,uBAAuB,GACxB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,0BAA0B,EAAE,MAAM,gCAAgC,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC"}
|
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { ListPage, Badge, DetailPage, Button, DetailSection, DetailRow, UpdatePage, EnumField, TextField, TextareaField, SaveFooter } from "anton-bakker-tailwind-components";
|
|
4
|
+
import { d } from "../../chunks/ingest-transport-DAA_pJNb.js";
|
|
5
|
+
import { c } from "../../chunks/normalisers-Cbd-i4Jh.js";
|
|
4
6
|
const STATUS_VARIANT = {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
+
new: "warning",
|
|
8
|
+
triaged: "default",
|
|
9
|
+
assigned: "default",
|
|
10
|
+
in_progress: "default",
|
|
11
|
+
fixed: "success",
|
|
12
|
+
pending_retest: "warning",
|
|
13
|
+
verified: "success",
|
|
14
|
+
closed: "success",
|
|
7
15
|
deferred: "secondary",
|
|
8
|
-
|
|
9
|
-
|
|
16
|
+
rejected: "destructive",
|
|
17
|
+
duplicate: "secondary",
|
|
18
|
+
reopened: "warning"
|
|
10
19
|
};
|
|
11
20
|
const TYPE_VARIANT = {
|
|
12
21
|
bug: "destructive",
|
|
@@ -21,7 +30,7 @@ function TypeBadge({ type }) {
|
|
|
21
30
|
return /* @__PURE__ */ jsx(Badge, { variant: TYPE_VARIANT[type] ?? "secondary", children: type });
|
|
22
31
|
}
|
|
23
32
|
const FeedbackListPage = React.forwardRef(
|
|
24
|
-
({ items, loading = false, onNavigate, className, emptyStateMessage, currentUser
|
|
33
|
+
({ items, loading = false, onNavigate, className, emptyStateMessage, currentUser: _currentUser }, ref) => {
|
|
25
34
|
const [search, setSearch] = React.useState("");
|
|
26
35
|
const [statusFilter, setStatusFilter] = React.useState("");
|
|
27
36
|
const [typeFilter, setTypeFilter] = React.useState("");
|
|
@@ -135,7 +144,7 @@ function parseStructuredContent(content) {
|
|
|
135
144
|
return { header, fields, raw: content };
|
|
136
145
|
}
|
|
137
146
|
const FeedbackDetailPage = React.forwardRef(
|
|
138
|
-
({ feedbackId, fetchItem, getScreenshotUrl, onNavigate, currentUser, className }, ref) => {
|
|
147
|
+
({ feedbackId, fetchItem, getScreenshotUrl, onNavigate, currentUser: _currentUser, className }, ref) => {
|
|
139
148
|
const [item, setItem] = React.useState(null);
|
|
140
149
|
const [loading, setLoading] = React.useState(true);
|
|
141
150
|
const [error, setError] = React.useState(null);
|
|
@@ -224,15 +233,22 @@ const FeedbackDetailPage = React.forwardRef(
|
|
|
224
233
|
}
|
|
225
234
|
);
|
|
226
235
|
FeedbackDetailPage.displayName = "FeedbackDetailPage";
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
236
|
+
const QA_LIFECYCLE_TRANSITIONS = {
|
|
237
|
+
new: ["triaged"],
|
|
238
|
+
triaged: ["assigned", "deferred", "rejected", "duplicate"],
|
|
239
|
+
assigned: ["in_progress", "deferred"],
|
|
240
|
+
in_progress: ["fixed", "deferred"],
|
|
241
|
+
fixed: ["pending_retest"],
|
|
242
|
+
pending_retest: ["verified", "reopened"],
|
|
243
|
+
verified: ["closed", "reopened"],
|
|
244
|
+
closed: ["reopened"],
|
|
245
|
+
rejected: ["reopened"],
|
|
246
|
+
duplicate: ["reopened"],
|
|
247
|
+
reopened: ["triaged", "assigned", "in_progress"],
|
|
248
|
+
deferred: ["triaged"]
|
|
233
249
|
};
|
|
234
250
|
function useStatusTransition(currentStatus) {
|
|
235
|
-
const allowedTargets =
|
|
251
|
+
const allowedTargets = [...QA_LIFECYCLE_TRANSITIONS[currentStatus] ?? []];
|
|
236
252
|
return { allowedTargets };
|
|
237
253
|
}
|
|
238
254
|
const STATUS_OPTIONS = [
|
|
@@ -266,7 +282,7 @@ const FeedbackUpdatePage = React.forwardRef(
|
|
|
266
282
|
const [item, setItem] = React.useState(null);
|
|
267
283
|
const [loading, setLoading] = React.useState(true);
|
|
268
284
|
const [saving, setSaving] = React.useState(false);
|
|
269
|
-
const [status, setStatus] = React.useState("
|
|
285
|
+
const [status, setStatus] = React.useState("new");
|
|
270
286
|
const [type, setType] = React.useState("bug");
|
|
271
287
|
const [scope, setScope] = React.useState("");
|
|
272
288
|
const [size, setSize] = React.useState("");
|
|
@@ -300,6 +316,10 @@ const FeedbackUpdatePage = React.forwardRef(
|
|
|
300
316
|
const isDirty = item && (status !== item.status || type !== item.type || scope !== (item.scope ?? "") || size !== (item.size ?? "") || assignedTo !== (item.assignedTo ?? "") || tags !== (item.tags?.join(", ") ?? ""));
|
|
301
317
|
const handleSave = async () => {
|
|
302
318
|
if (!item) return;
|
|
319
|
+
if (!updateItem) {
|
|
320
|
+
console.warn("FeedbackUpdatePage: updateItem prop not provided. This component is deprecated.");
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
303
323
|
setSaving(true);
|
|
304
324
|
try {
|
|
305
325
|
const patch = { lastModifiedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
@@ -310,23 +330,10 @@ const FeedbackUpdatePage = React.forwardRef(
|
|
|
310
330
|
if (assignedTo !== (item.assignedTo ?? "")) patch.assignedTo = assignedTo || void 0;
|
|
311
331
|
const newTags = tags.split(",").map((t) => t.trim()).filter(Boolean);
|
|
312
332
|
if (JSON.stringify(newTags) !== JSON.stringify(item.tags ?? [])) patch.tags = newTags;
|
|
313
|
-
if (status === "
|
|
333
|
+
if (status === "closed" && item.status !== "closed") {
|
|
314
334
|
patch.resolvedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
315
335
|
patch.resolvedBy = currentUser?.userId ?? "unknown";
|
|
316
336
|
}
|
|
317
|
-
if (classificationChanged && correctionNote) {
|
|
318
|
-
patch.corrections = [
|
|
319
|
-
...item.corrections ?? [],
|
|
320
|
-
{
|
|
321
|
-
field: [type !== item.type && "type", scope !== (item.scope ?? "") && "scope", size !== (item.size ?? "") && "size"].filter(Boolean).join(", "),
|
|
322
|
-
before: { type: item.type, scope: item.scope, size: item.size },
|
|
323
|
-
after: { type, scope: scope || void 0, size: size || void 0 },
|
|
324
|
-
correctedBy: currentUser?.userId ?? "unknown",
|
|
325
|
-
correctedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
326
|
-
reason: correctionNote
|
|
327
|
-
}
|
|
328
|
-
];
|
|
329
|
-
}
|
|
330
337
|
await updateItem(feedbackId, patch);
|
|
331
338
|
onNavigate(`/feedback/${feedbackId}`);
|
|
332
339
|
} finally {
|
|
@@ -346,7 +353,7 @@ const FeedbackUpdatePage = React.forwardRef(
|
|
|
346
353
|
Button,
|
|
347
354
|
{
|
|
348
355
|
onClick: handleSave,
|
|
349
|
-
disabled: saving || !isDirty
|
|
356
|
+
disabled: saving || !isDirty,
|
|
350
357
|
children: saving ? "Saving…" : "Save changes"
|
|
351
358
|
}
|
|
352
359
|
)
|
|
@@ -441,12 +448,288 @@ const FeedbackUpdatePage = React.forwardRef(
|
|
|
441
448
|
}
|
|
442
449
|
);
|
|
443
450
|
FeedbackUpdatePage.displayName = "FeedbackUpdatePage";
|
|
451
|
+
function ActivityTimeline({ activity, className }) {
|
|
452
|
+
if (activity.length === 0) {
|
|
453
|
+
return /* @__PURE__ */ jsx("div", { "data-testid": "activity-timeline", className, children: /* @__PURE__ */ jsx("div", { "data-testid": "activity-empty-state", children: "No activity yet." }) });
|
|
454
|
+
}
|
|
455
|
+
return /* @__PURE__ */ jsx("div", { "data-testid": "activity-timeline", className, children: activity.map((entry) => /* @__PURE__ */ jsxs("div", { "data-testid": "activity-entry", children: [
|
|
456
|
+
/* @__PURE__ */ jsx("span", { "data-testid": "activity-kind-badge", children: entry.kind.replace(/_/g, " ") }),
|
|
457
|
+
/* @__PURE__ */ jsx("span", { "data-testid": "activity-actor", children: entry.actor.displayName }),
|
|
458
|
+
/* @__PURE__ */ jsx("span", { "data-testid": "activity-timestamp", children: entry.occurredAt }),
|
|
459
|
+
entry.body !== void 0 && /* @__PURE__ */ jsx("span", { "data-testid": "activity-body", children: entry.body })
|
|
460
|
+
] }, entry.id)) });
|
|
461
|
+
}
|
|
462
|
+
const AnnotationComposer = React.forwardRef(
|
|
463
|
+
(props, ref) => {
|
|
464
|
+
const { feedbackId, currentUser, can: can2, appendActivity, appSource, onAppended } = props;
|
|
465
|
+
const canComment = can2("feedback:comment");
|
|
466
|
+
const canAnnotate = can2("feedback:annotate");
|
|
467
|
+
const [body, setBody] = React.useState("");
|
|
468
|
+
const [conflictReason, setConflictReason] = React.useState(void 0);
|
|
469
|
+
const [submitting, setSubmitting] = React.useState(false);
|
|
470
|
+
if (!canComment && !canAnnotate) return null;
|
|
471
|
+
const submit = async (kind) => {
|
|
472
|
+
setSubmitting(true);
|
|
473
|
+
setConflictReason(void 0);
|
|
474
|
+
const entry = {
|
|
475
|
+
id: crypto.randomUUID(),
|
|
476
|
+
actor: { userId: currentUser.userId, displayName: currentUser.name ?? currentUser.userId },
|
|
477
|
+
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
478
|
+
recordedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
479
|
+
source: appSource,
|
|
480
|
+
kind,
|
|
481
|
+
body,
|
|
482
|
+
schemaVersion: 1
|
|
483
|
+
};
|
|
484
|
+
const result = await appendActivity(feedbackId, entry);
|
|
485
|
+
if (result.success) {
|
|
486
|
+
setBody("");
|
|
487
|
+
setConflictReason(void 0);
|
|
488
|
+
onAppended?.();
|
|
489
|
+
} else {
|
|
490
|
+
setConflictReason(result.conflictReason ?? result.error ?? "Unknown error");
|
|
491
|
+
}
|
|
492
|
+
setSubmitting(false);
|
|
493
|
+
};
|
|
494
|
+
const isDisabled = body.trim().length === 0 || submitting;
|
|
495
|
+
return /* @__PURE__ */ jsxs("div", { ref, "data-testid": "annotation-composer", className: "space-y-3", children: [
|
|
496
|
+
/* @__PURE__ */ jsx(
|
|
497
|
+
"textarea",
|
|
498
|
+
{
|
|
499
|
+
"data-testid": "annotation-textarea",
|
|
500
|
+
value: body,
|
|
501
|
+
onChange: (e) => setBody(e.target.value),
|
|
502
|
+
placeholder: "Add a comment, note, question, or answer...",
|
|
503
|
+
className: "w-full min-h-[100px] p-3 border border-input rounded-md bg-background text-sm resize-y focus:outline-none focus:ring-2 focus:ring-ring"
|
|
504
|
+
}
|
|
505
|
+
),
|
|
506
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [
|
|
507
|
+
canComment && /* @__PURE__ */ jsx(
|
|
508
|
+
Button,
|
|
509
|
+
{
|
|
510
|
+
"data-testid": "annotation-comment-button",
|
|
511
|
+
disabled: isDisabled,
|
|
512
|
+
onClick: () => {
|
|
513
|
+
void submit("comment");
|
|
514
|
+
},
|
|
515
|
+
variant: "default",
|
|
516
|
+
size: "sm",
|
|
517
|
+
children: "Comment"
|
|
518
|
+
}
|
|
519
|
+
),
|
|
520
|
+
canAnnotate && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
521
|
+
/* @__PURE__ */ jsx(
|
|
522
|
+
Button,
|
|
523
|
+
{
|
|
524
|
+
"data-testid": "annotation-note-button",
|
|
525
|
+
disabled: isDisabled,
|
|
526
|
+
onClick: () => {
|
|
527
|
+
void submit("note");
|
|
528
|
+
},
|
|
529
|
+
variant: "outline",
|
|
530
|
+
size: "sm",
|
|
531
|
+
children: "Note"
|
|
532
|
+
}
|
|
533
|
+
),
|
|
534
|
+
/* @__PURE__ */ jsx(
|
|
535
|
+
Button,
|
|
536
|
+
{
|
|
537
|
+
"data-testid": "annotation-question-button",
|
|
538
|
+
disabled: isDisabled,
|
|
539
|
+
onClick: () => {
|
|
540
|
+
void submit("question");
|
|
541
|
+
},
|
|
542
|
+
variant: "outline",
|
|
543
|
+
size: "sm",
|
|
544
|
+
children: "Question"
|
|
545
|
+
}
|
|
546
|
+
),
|
|
547
|
+
/* @__PURE__ */ jsx(
|
|
548
|
+
Button,
|
|
549
|
+
{
|
|
550
|
+
"data-testid": "annotation-answer-button",
|
|
551
|
+
disabled: isDisabled,
|
|
552
|
+
onClick: () => {
|
|
553
|
+
void submit("answer");
|
|
554
|
+
},
|
|
555
|
+
variant: "outline",
|
|
556
|
+
size: "sm",
|
|
557
|
+
children: "Answer"
|
|
558
|
+
}
|
|
559
|
+
)
|
|
560
|
+
] })
|
|
561
|
+
] }),
|
|
562
|
+
conflictReason !== void 0 && /* @__PURE__ */ jsx("div", { "data-testid": "annotation-conflict-reason", className: "text-sm text-destructive", children: conflictReason })
|
|
563
|
+
] });
|
|
564
|
+
}
|
|
565
|
+
);
|
|
566
|
+
AnnotationComposer.displayName = "AnnotationComposer";
|
|
567
|
+
const CAPABILITY_MAP = {
|
|
568
|
+
triaged: "feedback:triage",
|
|
569
|
+
assigned: "feedback:assign",
|
|
570
|
+
in_progress: "feedback:assign",
|
|
571
|
+
fixed: "feedback:resolve",
|
|
572
|
+
pending_retest: "feedback:resolve",
|
|
573
|
+
verified: "feedback:verify",
|
|
574
|
+
closed: "feedback:resolve",
|
|
575
|
+
deferred: "feedback:triage",
|
|
576
|
+
rejected: "feedback:reject",
|
|
577
|
+
duplicate: "feedback:reject",
|
|
578
|
+
reopened: "feedback:reopen"
|
|
579
|
+
};
|
|
580
|
+
function formatStatusLabel(status) {
|
|
581
|
+
return status.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
582
|
+
}
|
|
583
|
+
const LifecycleControls = React.forwardRef(
|
|
584
|
+
(props, ref) => {
|
|
585
|
+
const { feedbackId, item, can: can2, transitionStatus, onTransitioned } = props;
|
|
586
|
+
const { allowedTargets } = useStatusTransition(item.status);
|
|
587
|
+
const authorizedTargets = allowedTargets.filter((target) => {
|
|
588
|
+
const cap = CAPABILITY_MAP[target];
|
|
589
|
+
return cap !== void 0 && can2(cap);
|
|
590
|
+
});
|
|
591
|
+
const [duplicateInputVisible, setDuplicateInputVisible] = React.useState(false);
|
|
592
|
+
const [duplicateOf, setDuplicateOf] = React.useState("");
|
|
593
|
+
const [conflictReason, setConflictReason] = React.useState(void 0);
|
|
594
|
+
const [submitting, setSubmitting] = React.useState(false);
|
|
595
|
+
if (authorizedTargets.length === 0) return null;
|
|
596
|
+
const handleTransition = async (target) => {
|
|
597
|
+
if (target === "duplicate") {
|
|
598
|
+
if (!duplicateInputVisible) {
|
|
599
|
+
setDuplicateInputVisible(true);
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
if (duplicateOf.trim().length === 0) {
|
|
603
|
+
setConflictReason("Duplicate target ID is required");
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
setSubmitting(true);
|
|
607
|
+
setConflictReason(void 0);
|
|
608
|
+
const opts2 = {
|
|
609
|
+
currentStatus: item.status,
|
|
610
|
+
duplicateOf,
|
|
611
|
+
duplicateReporters: item.reporters
|
|
612
|
+
};
|
|
613
|
+
const result2 = await transitionStatus(feedbackId, "duplicate", opts2);
|
|
614
|
+
if (result2.success) {
|
|
615
|
+
setDuplicateInputVisible(false);
|
|
616
|
+
setDuplicateOf("");
|
|
617
|
+
setConflictReason(void 0);
|
|
618
|
+
onTransitioned?.();
|
|
619
|
+
} else {
|
|
620
|
+
setConflictReason(result2.conflictReason ?? result2.error ?? "Unknown error");
|
|
621
|
+
}
|
|
622
|
+
setSubmitting(false);
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
setSubmitting(true);
|
|
626
|
+
setConflictReason(void 0);
|
|
627
|
+
const opts = {
|
|
628
|
+
currentStatus: item.status
|
|
629
|
+
};
|
|
630
|
+
const result = await transitionStatus(feedbackId, target, opts);
|
|
631
|
+
if (result.success) {
|
|
632
|
+
setConflictReason(void 0);
|
|
633
|
+
onTransitioned?.();
|
|
634
|
+
} else {
|
|
635
|
+
setConflictReason(result.conflictReason ?? result.error ?? "Unknown error");
|
|
636
|
+
}
|
|
637
|
+
setSubmitting(false);
|
|
638
|
+
};
|
|
639
|
+
return /* @__PURE__ */ jsxs("div", { ref, "data-testid": "lifecycle-controls", className: "space-y-3", children: [
|
|
640
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-2 flex-wrap", children: authorizedTargets.map((target) => /* @__PURE__ */ jsx(
|
|
641
|
+
Button,
|
|
642
|
+
{
|
|
643
|
+
"data-testid": `lifecycle-${target}-button`,
|
|
644
|
+
onClick: () => {
|
|
645
|
+
void handleTransition(target);
|
|
646
|
+
},
|
|
647
|
+
disabled: submitting,
|
|
648
|
+
variant: target === "rejected" || target === "duplicate" ? "destructive" : "default",
|
|
649
|
+
size: "sm",
|
|
650
|
+
children: formatStatusLabel(target)
|
|
651
|
+
},
|
|
652
|
+
target
|
|
653
|
+
)) }),
|
|
654
|
+
duplicateInputVisible && /* @__PURE__ */ jsxs("div", { "data-testid": "duplicate-input-container", className: "space-y-2", children: [
|
|
655
|
+
/* @__PURE__ */ jsx(
|
|
656
|
+
"input",
|
|
657
|
+
{
|
|
658
|
+
"data-testid": "duplicate-of-input",
|
|
659
|
+
value: duplicateOf,
|
|
660
|
+
onChange: (e) => setDuplicateOf(e.target.value),
|
|
661
|
+
placeholder: "Canonical feedback ID (e.g., FB-2026-11-01-abc12345)",
|
|
662
|
+
className: "w-full p-2 border border-input rounded-md bg-background text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
|
663
|
+
}
|
|
664
|
+
),
|
|
665
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
666
|
+
/* @__PURE__ */ jsx(
|
|
667
|
+
Button,
|
|
668
|
+
{
|
|
669
|
+
onClick: () => {
|
|
670
|
+
void handleTransition("duplicate");
|
|
671
|
+
},
|
|
672
|
+
disabled: submitting || duplicateOf.trim().length === 0,
|
|
673
|
+
size: "sm",
|
|
674
|
+
children: "Confirm Duplicate"
|
|
675
|
+
}
|
|
676
|
+
),
|
|
677
|
+
/* @__PURE__ */ jsx(
|
|
678
|
+
Button,
|
|
679
|
+
{
|
|
680
|
+
onClick: () => {
|
|
681
|
+
setDuplicateInputVisible(false);
|
|
682
|
+
setDuplicateOf("");
|
|
683
|
+
setConflictReason(void 0);
|
|
684
|
+
},
|
|
685
|
+
disabled: submitting,
|
|
686
|
+
variant: "outline",
|
|
687
|
+
size: "sm",
|
|
688
|
+
children: "Cancel"
|
|
689
|
+
}
|
|
690
|
+
)
|
|
691
|
+
] })
|
|
692
|
+
] }),
|
|
693
|
+
conflictReason !== void 0 && /* @__PURE__ */ jsx("div", { "data-testid": "lifecycle-conflict-reason", className: "text-sm text-destructive", children: conflictReason })
|
|
694
|
+
] });
|
|
695
|
+
}
|
|
696
|
+
);
|
|
697
|
+
LifecycleControls.displayName = "LifecycleControls";
|
|
698
|
+
const CAPABILITIES = {
|
|
699
|
+
"feedback:comment": "feedback:comment",
|
|
700
|
+
"feedback:annotate": "feedback:annotate",
|
|
701
|
+
"feedback:triage": "feedback:triage",
|
|
702
|
+
"feedback:assign": "feedback:assign",
|
|
703
|
+
"feedback:resolve": "feedback:resolve",
|
|
704
|
+
"feedback:verify": "feedback:verify",
|
|
705
|
+
"feedback:reopen": "feedback:reopen",
|
|
706
|
+
"feedback:reject": "feedback:reject",
|
|
707
|
+
"feedback:delete": "feedback:delete"
|
|
708
|
+
};
|
|
709
|
+
function can(capability, item, user) {
|
|
710
|
+
if (user.capabilities.has(capability)) {
|
|
711
|
+
return true;
|
|
712
|
+
}
|
|
713
|
+
const isSubmitter = user.userId === item.submittedBy;
|
|
714
|
+
const isReporter = item.reporters.some((r) => r.userId === user.userId);
|
|
715
|
+
if ((isSubmitter || isReporter) && (capability === "feedback:comment" || capability === "feedback:annotate")) {
|
|
716
|
+
return true;
|
|
717
|
+
}
|
|
718
|
+
return false;
|
|
719
|
+
}
|
|
444
720
|
export {
|
|
721
|
+
ActivityTimeline,
|
|
722
|
+
AnnotationComposer,
|
|
723
|
+
CAPABILITIES,
|
|
445
724
|
FeedbackDetailPage,
|
|
446
725
|
FeedbackListPage,
|
|
447
726
|
FeedbackUpdatePage,
|
|
727
|
+
LifecycleControls,
|
|
448
728
|
StatusBadge,
|
|
449
729
|
TypeBadge,
|
|
730
|
+
can,
|
|
731
|
+
d as createIngestAnnotateClient,
|
|
732
|
+
c as normaliseStoredItem,
|
|
450
733
|
useStatusTransition
|
|
451
734
|
};
|
|
452
735
|
//# sourceMappingURL=index.js.map
|