@cellarnode/ui 0.186.0 → 0.188.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/dist/brand/brand-assets-api.d.ts +58 -0
- package/dist/brand/brand-assets-api.d.ts.map +1 -0
- package/dist/brand/brand-assets-api.js +160 -0
- package/dist/brand/brand-assets-api.js.map +1 -0
- package/dist/brand/brand-generation-api.d.ts +44 -0
- package/dist/brand/brand-generation-api.d.ts.map +1 -0
- package/dist/brand/brand-generation-api.js +2 -0
- package/dist/brand/brand-generation-api.js.map +1 -0
- package/dist/brand/brand-generation-client.d.ts +36 -0
- package/dist/brand/brand-generation-client.d.ts.map +1 -0
- package/dist/brand/brand-generation-client.js +107 -0
- package/dist/brand/brand-generation-client.js.map +1 -0
- package/dist/brand/brand-generation-copy.d.ts +44 -0
- package/dist/brand/brand-generation-copy.d.ts.map +1 -0
- package/dist/brand/brand-generation-copy.js +70 -0
- package/dist/brand/brand-generation-copy.js.map +1 -0
- package/dist/brand/brand-generation-fixtures.d.ts +32 -0
- package/dist/brand/brand-generation-fixtures.d.ts.map +1 -0
- package/dist/brand/brand-generation-fixtures.js +88 -0
- package/dist/brand/brand-generation-fixtures.js.map +1 -0
- package/dist/brand/brand-generation-flow.d.ts +34 -0
- package/dist/brand/brand-generation-flow.d.ts.map +1 -0
- package/dist/brand/brand-generation-flow.js +34 -0
- package/dist/brand/brand-generation-flow.js.map +1 -0
- package/dist/brand/brand-generation-keys.d.ts +16 -0
- package/dist/brand/brand-generation-keys.d.ts.map +1 -0
- package/dist/brand/brand-generation-keys.js +16 -0
- package/dist/brand/brand-generation-keys.js.map +1 -0
- package/dist/brand/brand-generation-progress.d.ts +27 -0
- package/dist/brand/brand-generation-progress.d.ts.map +1 -0
- package/dist/brand/brand-generation-progress.js +61 -0
- package/dist/brand/brand-generation-progress.js.map +1 -0
- package/dist/brand/brand-generation-recovery-error.d.ts +8 -0
- package/dist/brand/brand-generation-recovery-error.d.ts.map +1 -0
- package/dist/brand/brand-generation-recovery-error.js +7 -0
- package/dist/brand/brand-generation-recovery-error.js.map +1 -0
- package/dist/brand/brand-generation-response.d.ts +86 -0
- package/dist/brand/brand-generation-response.d.ts.map +1 -0
- package/dist/brand/brand-generation-response.js +261 -0
- package/dist/brand/brand-generation-response.js.map +1 -0
- package/dist/brand/brand-generation-sse-parser.d.ts +14 -0
- package/dist/brand/brand-generation-sse-parser.d.ts.map +1 -0
- package/dist/brand/brand-generation-sse-parser.js +92 -0
- package/dist/brand/brand-generation-sse-parser.js.map +1 -0
- package/dist/brand/brand-generation-start.d.ts +49 -0
- package/dist/brand/brand-generation-start.d.ts.map +1 -0
- package/dist/brand/brand-generation-start.js +48 -0
- package/dist/brand/brand-generation-start.js.map +1 -0
- package/dist/brand/brand-generation-steps.d.ts +4 -0
- package/dist/brand/brand-generation-steps.d.ts.map +1 -0
- package/dist/brand/brand-generation-steps.js +29 -0
- package/dist/brand/brand-generation-steps.js.map +1 -0
- package/dist/brand/brand-generation-surface.d.ts +50 -0
- package/dist/brand/brand-generation-surface.d.ts.map +1 -0
- package/dist/brand/brand-generation-surface.js +25 -0
- package/dist/brand/brand-generation-surface.js.map +1 -0
- package/dist/brand/brand-generation-url.d.ts +6 -0
- package/dist/brand/brand-generation-url.d.ts.map +1 -0
- package/dist/brand/brand-generation-url.js +4 -0
- package/dist/brand/brand-generation-url.js.map +1 -0
- package/dist/brand/brand-image-chooser.d.ts +3 -1
- package/dist/brand/brand-image-chooser.d.ts.map +1 -1
- package/dist/brand/brand-image-chooser.js +7 -1
- package/dist/brand/brand-image-chooser.js.map +1 -1
- package/dist/brand/brand-image-choosers.d.ts +37 -0
- package/dist/brand/brand-image-choosers.d.ts.map +1 -0
- package/dist/brand/brand-image-choosers.js +76 -0
- package/dist/brand/brand-image-choosers.js.map +1 -0
- package/dist/brand/brand-workspace-change-notice.d.ts +20 -0
- package/dist/brand/brand-workspace-change-notice.d.ts.map +1 -0
- package/dist/brand/brand-workspace-change-notice.js +19 -0
- package/dist/brand/brand-workspace-change-notice.js.map +1 -0
- package/dist/brand/index.d.ts +37 -0
- package/dist/brand/index.d.ts.map +1 -1
- package/dist/brand/index.js +29 -0
- package/dist/brand/index.js.map +1 -1
- package/dist/brand/resume-brand-generation.d.ts +17 -0
- package/dist/brand/resume-brand-generation.d.ts.map +1 -0
- package/dist/brand/resume-brand-generation.js +22 -0
- package/dist/brand/resume-brand-generation.js.map +1 -0
- package/dist/brand/use-brand-generation-discovery.d.ts +14 -0
- package/dist/brand/use-brand-generation-discovery.d.ts.map +1 -0
- package/dist/brand/use-brand-generation-discovery.js +61 -0
- package/dist/brand/use-brand-generation-discovery.js.map +1 -0
- package/dist/brand/use-brand-generation-transport.d.ts +16 -0
- package/dist/brand/use-brand-generation-transport.d.ts.map +1 -0
- package/dist/brand/use-brand-generation-transport.js +182 -0
- package/dist/brand/use-brand-generation-transport.js.map +1 -0
- package/dist/brand/use-brand-generation.d.ts +35 -0
- package/dist/brand/use-brand-generation.d.ts.map +1 -0
- package/dist/brand/use-brand-generation.js +225 -0
- package/dist/brand/use-brand-generation.js.map +1 -0
- package/dist/brand/use-brand-image-operation-result.d.ts +43 -0
- package/dist/brand/use-brand-image-operation-result.d.ts.map +1 -0
- package/dist/brand/use-brand-image-operation-result.js +162 -0
- package/dist/brand/use-brand-image-operation-result.js.map +1 -0
- package/dist/brand/use-brand-profile-acceptance.d.ts +38 -0
- package/dist/brand/use-brand-profile-acceptance.d.ts.map +1 -0
- package/dist/brand/use-brand-profile-acceptance.js +84 -0
- package/dist/brand/use-brand-profile-acceptance.js.map +1 -0
- package/dist/brand/use-brand-public-visibility.d.ts +59 -0
- package/dist/brand/use-brand-public-visibility.d.ts.map +1 -0
- package/dist/brand/use-brand-public-visibility.js +69 -0
- package/dist/brand/use-brand-public-visibility.js.map +1 -0
- package/dist/brand/use-brand-workspace-concurrency.d.ts +132 -0
- package/dist/brand/use-brand-workspace-concurrency.d.ts.map +1 -0
- package/dist/brand/use-brand-workspace-concurrency.js +255 -0
- package/dist/brand/use-brand-workspace-concurrency.js.map +1 -0
- package/dist/brand/use-brand-workspace-image-candidates.d.ts +19 -0
- package/dist/brand/use-brand-workspace-image-candidates.d.ts.map +1 -0
- package/dist/brand/use-brand-workspace-image-candidates.js +39 -0
- package/dist/brand/use-brand-workspace-image-candidates.js.map +1 -0
- package/dist/brand/use-org-scoped-state.d.ts +3 -0
- package/dist/brand/use-org-scoped-state.d.ts.map +1 -0
- package/dist/brand/use-org-scoped-state.js +22 -0
- package/dist/brand/use-org-scoped-state.js.map +1 -0
- package/dist/components/multi-step-loader.d.ts +4 -0
- package/dist/components/multi-step-loader.d.ts.map +1 -1
- package/dist/components/multi-step-loader.js +4 -3
- package/dist/components/multi-step-loader.js.map +1 -1
- package/dist/moderation/moderation-event.d.ts +2 -0
- package/dist/moderation/moderation-event.d.ts.map +1 -1
- package/dist/moderation/moderation-event.js +7 -0
- package/dist/moderation/moderation-event.js.map +1 -1
- package/dist/moderation/moderation-store.d.ts.map +1 -1
- package/dist/moderation/moderation-store.js +4 -2
- package/dist/moderation/moderation-store.js.map +1 -1
- package/dist/realtime/job-status/schemas.d.ts +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { useCallback, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { GIVE_UP_AFTER_MS, useModerationCheckControls, useModerationOperation, } from "../moderation/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* CEL-2127 (from producer, CEL-1682/CEL-1681) — maps the CEL-1320 moderation read model's 8-state
|
|
5
|
+
* `ModerationStatus` (checking/passed/rejected/in-review/
|
|
6
|
+
* approved-after-review/changes-requested/unpublished/escalated) onto
|
|
7
|
+
* `BrandImageChooser`'s narrower `BrandImageOperationStatus` contract
|
|
8
|
+
* (saved/checking/passed/rejected/unknown/error). A future backend status
|
|
9
|
+
* this map doesn't know about degrades to "unknown" rather than being
|
|
10
|
+
* dropped, mirroring the shared event parser's forward-compatibility
|
|
11
|
+
* stance.
|
|
12
|
+
*/
|
|
13
|
+
function mapModerationStatus(status) {
|
|
14
|
+
switch (status) {
|
|
15
|
+
case "checking":
|
|
16
|
+
case "in-review":
|
|
17
|
+
return "checking";
|
|
18
|
+
case "passed":
|
|
19
|
+
case "approved-after-review":
|
|
20
|
+
return "passed";
|
|
21
|
+
case "rejected":
|
|
22
|
+
case "changes-requested":
|
|
23
|
+
return "rejected";
|
|
24
|
+
default:
|
|
25
|
+
return "unknown";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A server verdict may be stamped slightly before the client's own clock. Kept
|
|
30
|
+
* small: it is also the window in which a leftover verdict for a previous
|
|
31
|
+
* upload could be mistaken for this one when the event carries no `assetId`.
|
|
32
|
+
*/
|
|
33
|
+
const CLOCK_SKEW_TOLERANCE_MS = 2_000;
|
|
34
|
+
/**
|
|
35
|
+
* Owns the chooser's current upload identity and adapts the shared moderation
|
|
36
|
+
* operation (including its exact-asset durable read) to the chooser result.
|
|
37
|
+
*/
|
|
38
|
+
export function useBrandImageOperationResult(
|
|
39
|
+
/**
|
|
40
|
+
* The durable, asset-id-keyed read (`BrandAssetsApi.fetchModeration`) used
|
|
41
|
+
* when no live verdict event arrives.
|
|
42
|
+
*/
|
|
43
|
+
fetchModeration) {
|
|
44
|
+
const [operation, setOperation] = useState(null);
|
|
45
|
+
const { markChecking, clearChecking, beginOperation: beginStoreOperation, } = useModerationCheckControls();
|
|
46
|
+
const beganAtRef = useRef(null);
|
|
47
|
+
// Read through a ref: an inline `fetchModeration` must not change
|
|
48
|
+
// `fetchPersisted`'s identity every render and restart the durable poll.
|
|
49
|
+
const fetchModerationRef = useRef(fetchModeration);
|
|
50
|
+
fetchModerationRef.current = fetchModeration;
|
|
51
|
+
const fetchPersisted = useMemo(() => {
|
|
52
|
+
const profileId = operation?.subject.brandProfileId;
|
|
53
|
+
return profileId && operation
|
|
54
|
+
? () => fetchModerationRef.current(profileId, operation.id)
|
|
55
|
+
: undefined;
|
|
56
|
+
}, [operation]);
|
|
57
|
+
const subjectState = useModerationOperation({
|
|
58
|
+
match: operation?.subject ?? null,
|
|
59
|
+
identity: operation?.id,
|
|
60
|
+
fetchPersisted,
|
|
61
|
+
});
|
|
62
|
+
const startOperation = useCallback((operationId, subject, startSeq) => {
|
|
63
|
+
const now = Date.now();
|
|
64
|
+
// An inline verdict is published while the upload request is in flight,
|
|
65
|
+
// i.e. BEFORE `now`; anchor staleness at when the request began (minus
|
|
66
|
+
// clock skew), not at when its response came back.
|
|
67
|
+
// Only a begin that this start is paired with (it carries the store's
|
|
68
|
+
// `startSeq`) counts; a begin whose upload failed must not leak into
|
|
69
|
+
// a later, unrelated operation.
|
|
70
|
+
const began = startSeq !== undefined ? (beganAtRef.current ?? now) : now;
|
|
71
|
+
beganAtRef.current = null;
|
|
72
|
+
setOperation({
|
|
73
|
+
id: operationId,
|
|
74
|
+
subject,
|
|
75
|
+
appliedAt: now,
|
|
76
|
+
notBefore: began - CLOCK_SKEW_TOLERANCE_MS,
|
|
77
|
+
});
|
|
78
|
+
markChecking(subject, operationId, startSeq);
|
|
79
|
+
}, [markChecking]);
|
|
80
|
+
const beginOperation = useCallback(() => {
|
|
81
|
+
beganAtRef.current = Date.now();
|
|
82
|
+
return beginStoreOperation();
|
|
83
|
+
}, [beginStoreOperation]);
|
|
84
|
+
const reset = useCallback(() => {
|
|
85
|
+
setOperation((current) => {
|
|
86
|
+
if (current)
|
|
87
|
+
clearChecking(current.subject);
|
|
88
|
+
return null;
|
|
89
|
+
});
|
|
90
|
+
}, [clearChecking]);
|
|
91
|
+
const operationResult = useMemo(() => {
|
|
92
|
+
if (!operation)
|
|
93
|
+
return null;
|
|
94
|
+
// A verdict landed for this subject. Guard against a STALE result from a
|
|
95
|
+
// PREVIOUS operation on the same slot (e.g. re-uploading a rejected
|
|
96
|
+
// banner) — the chooser's own contract: "old verdict cannot complete
|
|
97
|
+
// newer upload." An event older than this operation's start is ignored,
|
|
98
|
+
// falling through to the in-flight branches below. CEL-1682 review
|
|
99
|
+
// fixup (reviewer A, P3): an UNPARSEABLE `occurredAt` must also count as
|
|
100
|
+
// stale — `Number.isFinite(NaN)` is `false`, so the original
|
|
101
|
+
// `finite && older` test let a malformed prior verdict slip through as
|
|
102
|
+
// "not stale" and complete a newer, still-in-flight upload.
|
|
103
|
+
if (subjectState.event) {
|
|
104
|
+
const event = subjectState.event;
|
|
105
|
+
// A verdict that names its asset is matched EXACTLY: it belongs to this
|
|
106
|
+
// operation iff it is this asset's, whatever the clocks say. A verdict
|
|
107
|
+
// that does not (the wire cannot always carry it) is matched on slot
|
|
108
|
+
// alone, so it must also not predate this operation: one older than the
|
|
109
|
+
// upload's start is a leftover from a PREVIOUS upload into the same slot
|
|
110
|
+
// ("old verdict cannot complete newer upload"). An unparseable
|
|
111
|
+
// `occurredAt` counts as stale for the same reason.
|
|
112
|
+
let isStale;
|
|
113
|
+
if (event.subject?.assetId !== undefined) {
|
|
114
|
+
isStale = event.subject.assetId !== operation.id;
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
const eventTime = Date.parse(event.occurredAt);
|
|
118
|
+
isStale = !Number.isFinite(eventTime) || eventTime < operation.notBefore;
|
|
119
|
+
}
|
|
120
|
+
if (!isStale) {
|
|
121
|
+
const status = mapModerationStatus(subjectState.event.state);
|
|
122
|
+
return {
|
|
123
|
+
status,
|
|
124
|
+
operationId: operation.id,
|
|
125
|
+
reason: status === "rejected"
|
|
126
|
+
? (subjectState.event.detail ?? subjectState.event.headline ?? null)
|
|
127
|
+
: undefined,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (subjectState.status === "checking") {
|
|
132
|
+
return {
|
|
133
|
+
status: subjectState.slow ? "unknown" : "checking",
|
|
134
|
+
operationId: operation.id,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
// A durable, asset-id-keyed read may resolve without a live event. The
|
|
138
|
+
// shared operation reconciler gives that answer precedence over a stale
|
|
139
|
+
// same-slot event; the chooser receives only the coarse status here.
|
|
140
|
+
if (subjectState.persistedOnly && subjectState.status) {
|
|
141
|
+
return { status: mapModerationStatus(subjectState.status), operationId: operation.id };
|
|
142
|
+
}
|
|
143
|
+
if (subjectState.status === "unknown" && !subjectState.event) {
|
|
144
|
+
return { status: "unknown", operationId: operation.id };
|
|
145
|
+
}
|
|
146
|
+
// No event yet, no local pending mark. Either `markChecking` hasn't run
|
|
147
|
+
// yet (a same-tick gap right after `startOperation`) or the store gave
|
|
148
|
+
// up waiting after GIVE_UP_AFTER_MS and dropped the mark — the store
|
|
149
|
+
// itself no longer distinguishes those, so use this operation's own
|
|
150
|
+
// `appliedAt` timestamp to tell them apart. This is a `useMemo`, so
|
|
151
|
+
// noticing the GIVE_UP transition depends on getting a fresh render —
|
|
152
|
+
// in practice the shared store's sweep interval delivers one while any
|
|
153
|
+
// check is pending, including the tick that drops the mark. This never polls
|
|
154
|
+
// `Date.now()` on its own between renders.
|
|
155
|
+
if (Date.now() - operation.appliedAt > GIVE_UP_AFTER_MS) {
|
|
156
|
+
return { status: "unknown", operationId: operation.id };
|
|
157
|
+
}
|
|
158
|
+
return { status: "saved", operationId: operation.id };
|
|
159
|
+
}, [operation, subjectState]);
|
|
160
|
+
return { operationResult, startOperation, beginOperation, reset };
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=use-brand-image-operation-result.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-brand-image-operation-result.js","sourceRoot":"","sources":["../../src/brand/use-brand-image-operation-result.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { UseBrandGenerationResult } from "./use-brand-generation.js";
|
|
2
|
+
export interface BrandWorkspaceNotice {
|
|
3
|
+
readonly tone: "success" | "error" | "info";
|
|
4
|
+
readonly message: string;
|
|
5
|
+
}
|
|
6
|
+
export interface BrandProfileAcceptanceCopy {
|
|
7
|
+
readonly acceptFailed: string;
|
|
8
|
+
readonly acceptEarlyRequested: string;
|
|
9
|
+
readonly acceptRunIncomplete: string;
|
|
10
|
+
readonly accepted: string;
|
|
11
|
+
}
|
|
12
|
+
export declare const DEFAULT_BRAND_PROFILE_ACCEPTANCE_COPY: BrandProfileAcceptanceCopy;
|
|
13
|
+
export interface UseBrandProfileAcceptanceOptions {
|
|
14
|
+
readonly orgId: string;
|
|
15
|
+
readonly brandGeneration: Pick<UseBrandGenerationResult, "job" | "accept" | "reset">;
|
|
16
|
+
/**
|
|
17
|
+
* Approves the generated draft (the injected data layer). With TanStack
|
|
18
|
+
* Query: `(handlers) => approve.mutate(undefined, handlers)`.
|
|
19
|
+
*/
|
|
20
|
+
readonly approve: (handlers: {
|
|
21
|
+
onSuccess: () => void;
|
|
22
|
+
onError: () => void;
|
|
23
|
+
}) => void;
|
|
24
|
+
readonly notify?: (notice: BrandWorkspaceNotice) => void;
|
|
25
|
+
readonly copy?: Partial<BrandProfileAcceptanceCopy>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* CEL-2127 — "Accept profile" for a generated draft, shared by producer and
|
|
29
|
+
* importer. While the run is still going it asks the backend to wrap up
|
|
30
|
+
* (early accept); once completed it approves the draft and resets the flow.
|
|
31
|
+
* Either path is single-flight per org and never reports into another org's
|
|
32
|
+
* session after an org switch.
|
|
33
|
+
*/
|
|
34
|
+
export declare function useBrandProfileAcceptance({ orgId, brandGeneration, approve, notify, copy, }: UseBrandProfileAcceptanceOptions): {
|
|
35
|
+
accept: () => void;
|
|
36
|
+
signalPending: boolean;
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=use-brand-profile-acceptance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-brand-profile-acceptance.d.ts","sourceRoot":"","sources":["../../src/brand/use-brand-profile-acceptance.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAEvE,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;IAC5C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,eAAO,MAAM,qCAAqC,EAAE,0BAMnD,CAAC;AAEF,MAAM,WAAW,gCAAgC;IAC/C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,eAAe,EAAE,IAAI,CAAC,wBAAwB,EAAE,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAC,CAAC;IACrF;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;QAAE,SAAS,EAAE,MAAM,IAAI,CAAC;QAAC,OAAO,EAAE,MAAM,IAAI,CAAA;KAAE,KAAK,IAAI,CAAC;IACrF,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACzD,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,0BAA0B,CAAC,CAAC;CACrD;AAID;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,EACxC,KAAK,EACL,eAAe,EACf,OAAO,EACP,MAAM,EACN,IAAI,GACL,EAAE,gCAAgC;;;EA4DlC"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { useRef, useState } from "react";
|
|
2
|
+
import { isTerminalStatus } from "./brand-generation-keys.js";
|
|
3
|
+
export const DEFAULT_BRAND_PROFILE_ACCEPTANCE_COPY = {
|
|
4
|
+
acceptFailed: "Failed to accept profile",
|
|
5
|
+
acceptEarlyRequested: "Wrapping up the current run",
|
|
6
|
+
acceptRunIncomplete: "This run did not finish, so there is no new profile to accept. Retry generation.",
|
|
7
|
+
accepted: "Profile accepted",
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* CEL-2127 — "Accept profile" for a generated draft, shared by producer and
|
|
11
|
+
* importer. While the run is still going it asks the backend to wrap up
|
|
12
|
+
* (early accept); once completed it approves the draft and resets the flow.
|
|
13
|
+
* Either path is single-flight per org and never reports into another org's
|
|
14
|
+
* session after an org switch.
|
|
15
|
+
*/
|
|
16
|
+
export function useBrandProfileAcceptance({ orgId, brandGeneration, approve, notify, copy, }) {
|
|
17
|
+
const text = { ...DEFAULT_BRAND_PROFILE_ACCEPTANCE_COPY, ...copy };
|
|
18
|
+
const [signalPendingState, setSignalPendingState] = useState(null);
|
|
19
|
+
const pendingActionRef = useRef(null);
|
|
20
|
+
const currentOrgIdRef = useRef(orgId);
|
|
21
|
+
currentOrgIdRef.current = orgId;
|
|
22
|
+
const signalPending = signalPendingState?.orgId === orgId;
|
|
23
|
+
const beginPendingAction = () => {
|
|
24
|
+
if (pendingActionRef.current?.orgId === orgId)
|
|
25
|
+
return null;
|
|
26
|
+
const action = { orgId };
|
|
27
|
+
pendingActionRef.current = action;
|
|
28
|
+
setSignalPendingState(action);
|
|
29
|
+
return action;
|
|
30
|
+
};
|
|
31
|
+
const finishPendingAction = (action) => {
|
|
32
|
+
if (pendingActionRef.current === action)
|
|
33
|
+
pendingActionRef.current = null;
|
|
34
|
+
setSignalPendingState((pending) => (pending === action ? null : pending));
|
|
35
|
+
};
|
|
36
|
+
const stillCurrent = (action) => currentOrgIdRef.current === action.orgId;
|
|
37
|
+
const accept = () => {
|
|
38
|
+
const job = brandGeneration.job;
|
|
39
|
+
if (job && !isTerminalStatus(job.status)) {
|
|
40
|
+
const action = beginPendingAction();
|
|
41
|
+
if (!action)
|
|
42
|
+
return;
|
|
43
|
+
brandGeneration
|
|
44
|
+
.accept()
|
|
45
|
+
.then((accepted) => {
|
|
46
|
+
if (!stillCurrent(action))
|
|
47
|
+
return;
|
|
48
|
+
if (accepted === false)
|
|
49
|
+
notify?.({ tone: "error", message: text.acceptFailed });
|
|
50
|
+
else
|
|
51
|
+
notify?.({ tone: "info", message: text.acceptEarlyRequested });
|
|
52
|
+
})
|
|
53
|
+
.catch(() => {
|
|
54
|
+
if (stillCurrent(action))
|
|
55
|
+
notify?.({ tone: "error", message: text.acceptFailed });
|
|
56
|
+
})
|
|
57
|
+
.finally(() => finishPendingAction(action));
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (job && job.status !== "completed") {
|
|
61
|
+
notify?.({ tone: "error", message: text.acceptRunIncomplete });
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const action = beginPendingAction();
|
|
65
|
+
if (!action)
|
|
66
|
+
return;
|
|
67
|
+
approve({
|
|
68
|
+
onSuccess: () => {
|
|
69
|
+
finishPendingAction(action);
|
|
70
|
+
if (!stillCurrent(action))
|
|
71
|
+
return;
|
|
72
|
+
notify?.({ tone: "success", message: text.accepted });
|
|
73
|
+
brandGeneration.reset();
|
|
74
|
+
},
|
|
75
|
+
onError: () => {
|
|
76
|
+
finishPendingAction(action);
|
|
77
|
+
if (stillCurrent(action))
|
|
78
|
+
notify?.({ tone: "error", message: text.acceptFailed });
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
return { accept, signalPending };
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=use-brand-profile-acceptance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-brand-profile-acceptance.js","sourceRoot":"","sources":["../../src/brand/use-brand-profile-acceptance.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { BrandWorkspaceTarget } from "./use-brand-workspace-concurrency.js";
|
|
2
|
+
import type { BrandPublicField } from "./brand-profile-review.js";
|
|
3
|
+
/** The profile a visibility write is compare-and-swapped against. */
|
|
4
|
+
export interface BrandVisibilityProfile {
|
|
5
|
+
readonly id: string;
|
|
6
|
+
readonly status: string;
|
|
7
|
+
readonly revision?: number | null;
|
|
8
|
+
}
|
|
9
|
+
export interface BrandPublicVisibilityWrite {
|
|
10
|
+
readonly expectedProfileId: string;
|
|
11
|
+
readonly expectedStatus: string;
|
|
12
|
+
readonly expectedRevision: number | undefined;
|
|
13
|
+
readonly publicVisibility: Readonly<Partial<Record<BrandPublicField, boolean>>>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The injected data layer: persist one field's visibility on the approved
|
|
17
|
+
* profile or on the generated draft, resolving with the saved profile.
|
|
18
|
+
*/
|
|
19
|
+
export interface BrandPublicVisibilityApi {
|
|
20
|
+
save: (write: BrandPublicVisibilityWrite, target: BrandWorkspaceTarget) => Promise<{
|
|
21
|
+
readonly revision?: number | null;
|
|
22
|
+
} | null | undefined | void>;
|
|
23
|
+
}
|
|
24
|
+
export type BrandPublicVisibilityEvent = {
|
|
25
|
+
readonly kind: "reload-before-saving";
|
|
26
|
+
} | {
|
|
27
|
+
readonly kind: "wait-for-save";
|
|
28
|
+
} | {
|
|
29
|
+
readonly kind: "save-failed";
|
|
30
|
+
};
|
|
31
|
+
export interface UseBrandPublicVisibilityOptions {
|
|
32
|
+
readonly orgId: string | null;
|
|
33
|
+
readonly api: BrandPublicVisibilityApi;
|
|
34
|
+
/** The approved profile. */
|
|
35
|
+
readonly profile: BrandVisibilityProfile | null | undefined;
|
|
36
|
+
/** The generated draft under review; wins over `profile` when present. */
|
|
37
|
+
readonly generatedDraftProfile: BrandVisibilityProfile | null | undefined;
|
|
38
|
+
/** `requiresReload` from `useBrandWorkspaceConcurrency`. */
|
|
39
|
+
readonly requiresReload: boolean;
|
|
40
|
+
/** A profile write (save, approve, discard) in flight. */
|
|
41
|
+
readonly profileWritePending: boolean;
|
|
42
|
+
/** A 409 REVISION_CONFLICT / TARGET_MISMATCH: wire to `markServerChanged`. */
|
|
43
|
+
readonly onConflict: () => void;
|
|
44
|
+
/** Wire to `noteOwnWrite` so the refetch is not read as a remote change. */
|
|
45
|
+
readonly onOwnWrite?: (saved: {
|
|
46
|
+
readonly revision?: number | null;
|
|
47
|
+
} | null | undefined) => void;
|
|
48
|
+
readonly notify?: (event: BrandPublicVisibilityEvent) => void;
|
|
49
|
+
/** Same duck-typed check as the concurrency hook's default classifier. */
|
|
50
|
+
readonly isConflict?: (error: unknown) => boolean;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* CEL-2127 — the public-visibility toggle handler for the brand workspace.
|
|
54
|
+
* One in-flight write per org+field; refuses while a reload is required or a
|
|
55
|
+
* profile write is pending; a CAS conflict marks the server as changed rather
|
|
56
|
+
* than retrying (the user must see the latest values first).
|
|
57
|
+
*/
|
|
58
|
+
export declare function useBrandPublicVisibility(options: UseBrandPublicVisibilityOptions): (field: BrandPublicField, next: boolean) => void;
|
|
59
|
+
//# sourceMappingURL=use-brand-public-visibility.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-brand-public-visibility.d.ts","sourceRoot":"","sources":["../../src/brand/use-brand-public-visibility.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE/D,qEAAqE;AACrE,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9C,QAAQ,CAAC,gBAAgB,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;CACjF;AAED;;;GAGG;AACH,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,CACJ,KAAK,EAAE,0BAA0B,EACjC,MAAM,EAAE,oBAAoB,KACzB,OAAO,CAAC;QAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAAC,CAAC;CAC/E;AAED,MAAM,MAAM,0BAA0B,GAClC;IAAE,QAAQ,CAAC,IAAI,EAAE,sBAAsB,CAAA;CAAE,GACzC;IAAE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAA;CAAE,GAClC;IAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAA;CAAE,CAAC;AAErC,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,GAAG,EAAE,wBAAwB,CAAC;IACvC,4BAA4B;IAC5B,QAAQ,CAAC,OAAO,EAAE,sBAAsB,GAAG,IAAI,GAAG,SAAS,CAAC;IAC5D,0EAA0E;IAC1E,QAAQ,CAAC,qBAAqB,EAAE,sBAAsB,GAAG,IAAI,GAAG,SAAS,CAAC;IAC1E,4DAA4D;IAC5D,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,0DAA0D;IAC1D,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;IACtC,8EAA8E;IAC9E,QAAQ,CAAC,UAAU,EAAE,MAAM,IAAI,CAAC;IAChC,4EAA4E;IAC5E,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,GAAG,SAAS,KAAK,IAAI,CAAC;IAChG,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,0BAA0B,KAAK,IAAI,CAAC;IAC9D,0EAA0E;IAC1E,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;CACnD;AAQD;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,+BAA+B,GACvC,CAAC,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,OAAO,KAAK,IAAI,CAmElD"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { useRef } from "react";
|
|
2
|
+
function defaultIsConflict(error) {
|
|
3
|
+
if (error === null || typeof error !== "object")
|
|
4
|
+
return false;
|
|
5
|
+
const code = error.code;
|
|
6
|
+
return code === "REVISION_CONFLICT" || code === "TARGET_MISMATCH";
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* CEL-2127 — the public-visibility toggle handler for the brand workspace.
|
|
10
|
+
* One in-flight write per org+field; refuses while a reload is required or a
|
|
11
|
+
* profile write is pending; a CAS conflict marks the server as changed rather
|
|
12
|
+
* than retrying (the user must see the latest values first).
|
|
13
|
+
*/
|
|
14
|
+
export function useBrandPublicVisibility(options) {
|
|
15
|
+
const pendingKeysRef = useRef(new Set());
|
|
16
|
+
const optionsRef = useRef(options);
|
|
17
|
+
optionsRef.current = options;
|
|
18
|
+
return (field, next) => {
|
|
19
|
+
const { orgId, api, profile, generatedDraftProfile, requiresReload, profileWritePending, onConflict, onOwnWrite, notify, isConflict = defaultIsConflict, } = optionsRef.current;
|
|
20
|
+
const reviewed = generatedDraftProfile ?? profile;
|
|
21
|
+
if (!orgId || !reviewed)
|
|
22
|
+
return;
|
|
23
|
+
if (requiresReload) {
|
|
24
|
+
notify?.({ kind: "reload-before-saving" });
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (profileWritePending) {
|
|
28
|
+
notify?.({ kind: "wait-for-save" });
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const pendingKey = `${orgId}:${field}`;
|
|
32
|
+
// Results of a write started in org A must never act in org B.
|
|
33
|
+
const stillCurrent = () => optionsRef.current.orgId === orgId;
|
|
34
|
+
if (pendingKeysRef.current.has(pendingKey))
|
|
35
|
+
return;
|
|
36
|
+
pendingKeysRef.current.add(pendingKey);
|
|
37
|
+
const target = generatedDraftProfile ? "draft" : "approved";
|
|
38
|
+
let attempt;
|
|
39
|
+
try {
|
|
40
|
+
attempt = api.save({
|
|
41
|
+
expectedProfileId: reviewed.id,
|
|
42
|
+
expectedStatus: reviewed.status,
|
|
43
|
+
expectedRevision: reviewed.revision ?? undefined,
|
|
44
|
+
publicVisibility: { [field]: next },
|
|
45
|
+
}, target);
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
attempt = Promise.reject(error);
|
|
49
|
+
}
|
|
50
|
+
void attempt
|
|
51
|
+
.then((saved) => {
|
|
52
|
+
if (stillCurrent())
|
|
53
|
+
onOwnWrite?.(saved ?? undefined);
|
|
54
|
+
}, (error) => {
|
|
55
|
+
if (!stillCurrent())
|
|
56
|
+
return;
|
|
57
|
+
if (isConflict(error)) {
|
|
58
|
+
onConflict();
|
|
59
|
+
notify?.({ kind: "reload-before-saving" });
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
notify?.({ kind: "save-failed" });
|
|
63
|
+
})
|
|
64
|
+
.finally(() => {
|
|
65
|
+
pendingKeysRef.current.delete(pendingKey);
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=use-brand-public-visibility.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-brand-public-visibility.js","sourceRoot":"","sources":["../../src/brand/use-brand-public-visibility.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import type { BrandProfileConflict } from "./brand-profile-form.js";
|
|
2
|
+
/**
|
|
3
|
+
* CEL-2127 — the ONE implementation of brand-workspace write concurrency.
|
|
4
|
+
*
|
|
5
|
+
* It owns what producer's `BrandContentController` and importer's
|
|
6
|
+
* `BrandProfileContent` each did by hand, with producer's semantics:
|
|
7
|
+
*
|
|
8
|
+
* - `409 REVISION_CONFLICT` surfaces as {@link UseBrandWorkspaceConcurrencyResult.conflict}
|
|
9
|
+
* on the form. The user then chooses, explicitly:
|
|
10
|
+
* - **reload** re-reads the server and remounts the form on the fresh values
|
|
11
|
+
* (the form key changes, so nothing of the stale edit survives);
|
|
12
|
+
* - **keep mine** re-submits the SAME sparse patch against the revision the
|
|
13
|
+
* conflict reported. It only ever runs from that explicit user action,
|
|
14
|
+
* never automatically, and the server still compare-and-swaps it, so a
|
|
15
|
+
* third write in between conflicts again instead of being overwritten.
|
|
16
|
+
* - `409 TARGET_MISMATCH` (the row this edit targeted is no longer the current
|
|
17
|
+
* target, e.g. the draft was approved elsewhere) has no revision to retry
|
|
18
|
+
* with: it reloads and closes the editor, and shows the change notice if
|
|
19
|
+
* that reload fails.
|
|
20
|
+
* - A server revision that moves underneath an open workspace, and is not our
|
|
21
|
+
* own write, raises `serverChanged` (render {@link BrandWorkspaceChangeNotice}).
|
|
22
|
+
*
|
|
23
|
+
* The data layer is injected: the hook never imports a client.
|
|
24
|
+
*/
|
|
25
|
+
/** What the hook needs to know about the profile the workspace is editing. */
|
|
26
|
+
export interface BrandWorkspaceSnapshot {
|
|
27
|
+
readonly id?: string | null;
|
|
28
|
+
readonly revision?: number | null;
|
|
29
|
+
readonly version?: number | null;
|
|
30
|
+
readonly updatedAt?: string | null;
|
|
31
|
+
readonly status?: string | null;
|
|
32
|
+
readonly profileData?: unknown;
|
|
33
|
+
}
|
|
34
|
+
export type BrandWorkspaceTarget = "approved" | "draft";
|
|
35
|
+
/** The parts of a write body the hook reads or rewrites. */
|
|
36
|
+
export interface BrandWorkspaceWriteBody {
|
|
37
|
+
expectedRevision?: number;
|
|
38
|
+
}
|
|
39
|
+
/** How a failed save is classified. {@link classifyBrandProfileApiError} is the default. */
|
|
40
|
+
export type BrandWorkspaceSaveFailure = {
|
|
41
|
+
readonly kind: "revision-conflict";
|
|
42
|
+
readonly currentRevision: number;
|
|
43
|
+
} | {
|
|
44
|
+
readonly kind: "target-mismatch";
|
|
45
|
+
} | {
|
|
46
|
+
readonly kind: "validation";
|
|
47
|
+
readonly fieldErrors: Readonly<Record<string, string>>;
|
|
48
|
+
} | {
|
|
49
|
+
readonly kind: "other";
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Duck-typed classifier for the brand-profile API error both dashboards use
|
|
53
|
+
* (`status`, `code`, `currentRevision`, `fieldErrors`). It deliberately does
|
|
54
|
+
* not `instanceof` a consumer class, so one classifier serves every client.
|
|
55
|
+
*/
|
|
56
|
+
export declare function classifyBrandProfileApiError(error: unknown): BrandWorkspaceSaveFailure;
|
|
57
|
+
/** Events the consumer turns into its own (translated) toast. The hook has no strings. */
|
|
58
|
+
export type BrandWorkspaceEvent = {
|
|
59
|
+
readonly kind: "saved";
|
|
60
|
+
} | {
|
|
61
|
+
readonly kind: "save-failed";
|
|
62
|
+
} | {
|
|
63
|
+
readonly kind: "target-mismatch";
|
|
64
|
+
} | {
|
|
65
|
+
readonly kind: "reload-failed";
|
|
66
|
+
} | {
|
|
67
|
+
readonly kind: "reload-before-saving";
|
|
68
|
+
} | {
|
|
69
|
+
readonly kind: "wait-for-save";
|
|
70
|
+
};
|
|
71
|
+
export interface UseBrandWorkspaceConcurrencyOptions<TBody extends BrandWorkspaceWriteBody> {
|
|
72
|
+
readonly orgId: string;
|
|
73
|
+
/** The profile (approved or generated draft) the workspace is editing. */
|
|
74
|
+
readonly workspaceProfile: BrandWorkspaceSnapshot | null | undefined;
|
|
75
|
+
/** Whether a form is open on `workspaceProfile`; changes only matter then. */
|
|
76
|
+
readonly workspaceVisible: boolean;
|
|
77
|
+
/** Any write of this workspace in flight (save, visibility, approve, discard). */
|
|
78
|
+
readonly localWritePending: boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Performs the write. Resolve with the saved profile. Its `revision` is
|
|
81
|
+
* REQUIRED for exact own-write detection: the refetch is "ours" only when
|
|
82
|
+
* the profile's revision equals it; without a revision the refetch raises
|
|
83
|
+
* the change notice (a safe false positive, never a swallowed remote edit).
|
|
84
|
+
* Reject with the transport error.
|
|
85
|
+
*/
|
|
86
|
+
readonly save: (body: TBody, target: BrandWorkspaceTarget) => Promise<{
|
|
87
|
+
readonly revision?: number | null;
|
|
88
|
+
} | null | undefined | void>;
|
|
89
|
+
/**
|
|
90
|
+
* Forces a re-read of everything the workspace derives from. MUST reject on
|
|
91
|
+
* failure so a failed reload never unlocks writes against a stale target.
|
|
92
|
+
* Resolve with the fresh workspace profile when known: it becomes the
|
|
93
|
+
* baseline, so the re-read is not reported back as a remote change.
|
|
94
|
+
*/
|
|
95
|
+
readonly reload: () => Promise<BrandWorkspaceSnapshot | null | undefined | void>;
|
|
96
|
+
readonly classifyError?: (error: unknown) => BrandWorkspaceSaveFailure;
|
|
97
|
+
/** Called after a successful save (close the editor here). */
|
|
98
|
+
readonly onSaved?: () => void;
|
|
99
|
+
/** Called after a successful reload; `closeEditor` is true for conflict resolution. */
|
|
100
|
+
readonly onReloaded?: (info: {
|
|
101
|
+
readonly closeEditor: boolean;
|
|
102
|
+
}) => void;
|
|
103
|
+
readonly notify?: (event: BrandWorkspaceEvent) => void;
|
|
104
|
+
}
|
|
105
|
+
export interface UseBrandWorkspaceConcurrencyResult<TBody extends BrandWorkspaceWriteBody> {
|
|
106
|
+
/** Pass to `BrandProfileForm`/`BrandProfileWorkspace`/`BrandGenerationSurface`. */
|
|
107
|
+
readonly conflict: BrandProfileConflict | null;
|
|
108
|
+
readonly fieldErrors: Readonly<Record<string, string>> | undefined;
|
|
109
|
+
/** The server changed underneath an open workspace; render the change notice. */
|
|
110
|
+
readonly serverChanged: boolean;
|
|
111
|
+
readonly reloadPending: boolean;
|
|
112
|
+
/** True while a write must wait for the user to reload (changed, reloading or conflicted). */
|
|
113
|
+
readonly requiresReload: boolean;
|
|
114
|
+
/** Pass as `key` on the form: it changes on every successful reload, remounting on fresh values. */
|
|
115
|
+
readonly workspaceKey: string;
|
|
116
|
+
/** Guarded submit: refuses while a reload is required or a write is pending. */
|
|
117
|
+
readonly submit: (body: TBody, target: BrandWorkspaceTarget) => void;
|
|
118
|
+
/** Conflict action: discard local edits, reload, close the editor. */
|
|
119
|
+
readonly reloadConflict: () => void;
|
|
120
|
+
/** Conflict action: deliberately re-submit the last patch against the reported revision. */
|
|
121
|
+
readonly keepMine: () => void;
|
|
122
|
+
/** Change-notice action: reload and keep the editor open. */
|
|
123
|
+
readonly reloadLatest: () => void;
|
|
124
|
+
/** Record a write made OUTSIDE `submit` (e.g. visibility) so its refetch is not a remote change. */
|
|
125
|
+
readonly noteOwnWrite: (saved: {
|
|
126
|
+
readonly revision?: number | null;
|
|
127
|
+
} | null | undefined) => void;
|
|
128
|
+
/** Mark the server as changed (e.g. a visibility write hit a conflict). */
|
|
129
|
+
readonly markServerChanged: () => void;
|
|
130
|
+
}
|
|
131
|
+
export declare function useBrandWorkspaceConcurrency<TBody extends BrandWorkspaceWriteBody>(options: UseBrandWorkspaceConcurrencyOptions<TBody>): UseBrandWorkspaceConcurrencyResult<TBody>;
|
|
132
|
+
//# sourceMappingURL=use-brand-workspace-concurrency.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-brand-workspace-concurrency.d.ts","sourceRoot":"","sources":["../../src/brand/use-brand-workspace-concurrency.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAIjE;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,8EAA8E;AAC9E,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,MAAM,MAAM,oBAAoB,GAAG,UAAU,GAAG,OAAO,CAAC;AAExD,4DAA4D;AAC5D,MAAM,WAAW,uBAAuB;IACtC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,4FAA4F;AAC5F,MAAM,MAAM,yBAAyB,GACjC;IAAE,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IAAC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;CAAE,GACxE;IAAE,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAA;CAAE,GACpC;IAAE,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CAAE,GACvF;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC;AAE/B;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,GAAG,yBAAyB,CAqBtF;AAED,0FAA0F;AAC1F,MAAM,MAAM,mBAAmB,GAC3B;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAC1B;IAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAA;CAAE,GAChC;IAAE,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAA;CAAE,GACpC;IAAE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAA;CAAE,GAClC;IAAE,QAAQ,CAAC,IAAI,EAAE,sBAAsB,CAAA;CAAE,GACzC;IAAE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAA;CAAE,CAAC;AAEvC,MAAM,WAAW,mCAAmC,CAAC,KAAK,SAAS,uBAAuB;IACxF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,0EAA0E;IAC1E,QAAQ,CAAC,gBAAgB,EAAE,sBAAsB,GAAG,IAAI,GAAG,SAAS,CAAC;IACrE,8EAA8E;IAC9E,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;IACnC,kFAAkF;IAClF,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;IACpC;;;;;;OAMG;IACH,QAAQ,CAAC,IAAI,EAAE,CACb,IAAI,EAAE,KAAK,EACX,MAAM,EAAE,oBAAoB,KACzB,OAAO,CAAC;QAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAAC,CAAC;IAC9E;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,sBAAsB,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAAC,CAAC;IACjF,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,yBAAyB,CAAC;IACvE,8DAA8D;IAC9D,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IAC9B,uFAAuF;IACvF,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC;IACxE,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,CAAC;CACxD;AAED,MAAM,WAAW,kCAAkC,CAAC,KAAK,SAAS,uBAAuB;IACvF,mFAAmF;IACnF,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAC/C,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,CAAC;IACnE,iFAAiF;IACjF,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,8FAA8F;IAC9F,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,oGAAoG;IACpG,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,gFAAgF;IAChF,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACrE,sEAAsE;IACtE,QAAQ,CAAC,cAAc,EAAE,MAAM,IAAI,CAAC;IACpC,4FAA4F;IAC5F,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAC;IAC9B,6DAA6D;IAC7D,QAAQ,CAAC,YAAY,EAAE,MAAM,IAAI,CAAC;IAClC,oGAAoG;IACpG,QAAQ,CAAC,YAAY,EAAE,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,GAAG,SAAS,KAAK,IAAI,CAAC;IACjG,2EAA2E;IAC3E,QAAQ,CAAC,iBAAiB,EAAE,MAAM,IAAI,CAAC;CACxC;AAyBD,wBAAgB,4BAA4B,CAAC,KAAK,SAAS,uBAAuB,EAChF,OAAO,EAAE,mCAAmC,CAAC,KAAK,CAAC,GAClD,kCAAkC,CAAC,KAAK,CAAC,CAsN3C"}
|