@pitcher/js-api 1.30.0 → 1.31.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/js-api.esm.js +80 -17
- package/js-api.esm.js.map +1 -1
- package/js-api.umd.min.js +15 -15
- package/js-api.umd.min.js.map +1 -1
- package/lib/apps/browser/components/ContextMenu/TranslateFileButton.test.d.ts +1 -0
- package/lib/apps/browser/stores/api.filesFoldersDeleter.test.d.ts +1 -0
- package/lib/apps/canvas-builder/components/container/PopupApps/PopupAppModal.spec.d.ts +1 -0
- package/lib/apps/canvas-builder/components/container/PresentationTools/PresentationToolsButtons.spec.d.ts +1 -0
- package/lib/apps/canvas-builder/components/ui/BottomSheetModal/BottomSheetModal.integration.spec.d.ts +1 -0
- package/lib/apps/canvas-builder/components/ui/BottomSheetModal/BottomSheetModal.spec.d.ts +1 -0
- package/lib/apps/canvas-builder/components/ui/DynamicContent/section-component-bindings.util.d.ts +32 -0
- package/lib/apps/canvas-builder/components/ui/DynamicContent/useDynamicContent.d.ts +4 -0
- package/lib/apps/canvas-builder/components/ui/Link/Link.util.d.ts +8 -1
- package/lib/apps/canvas-builder/components/ui/Link/Link.util.test.d.ts +1 -0
- package/lib/apps/canvas-builder/components/ui/SectionList/SectionItemModal.spec.d.ts +1 -0
- package/lib/apps/canvas-builder/composables/useCanvas.d.ts +83 -9
- package/lib/apps/canvas-builder/composables/useCanvasBlocks.d.ts +4 -2
- package/lib/apps/canvas-builder/composables/useCanvasHistory.d.ts +2 -2
- package/lib/apps/canvas-builder/composables/useCanvasTheme.d.ts +2 -2
- package/lib/apps/canvas-builder/composables/useNavigatorDrawer.d.ts +25 -0
- package/lib/apps/canvas-builder/composables/useNavigatorDrawer.spec.d.ts +1 -0
- package/lib/apps/canvas-builder/composables/usePopupApps.d.ts +41 -4
- package/lib/apps/canvas-builder/composables/usePopupApps.spec.d.ts +1 -0
- package/lib/apps/canvas-builder/composables/usePresentationTools.d.ts +112 -0
- package/lib/apps/canvas-builder/composables/usePresentationTools.spec.d.ts +1 -0
- package/lib/apps/canvas-builder/types/canvas.d.ts +13 -0
- package/lib/apps/canvas-builder/util/canvas.util.d.ts +12 -0
- package/lib/apps/canvas-builder/util/completionWizard.util.d.ts +251 -11
- package/lib/apps/canvas-builder/util/iframe.util.d.ts +24 -2
- package/lib/apps/canvas-builder/util/iframe.util.test.d.ts +1 -0
- package/lib/apps/canvas-selector/stores/app.d.ts +14 -0
- package/lib/apps/collection-player/components/AppHeader.spec.d.ts +1 -0
- package/lib/apps/collection-player/composables/speaker-notes.use.d.ts +14 -0
- package/lib/apps/collection-player/composables/speaker-notes.use.spec.d.ts +1 -0
- package/lib/apps/collection-player/stores/app.store.d.ts +5 -1
- package/lib/apps/collection-player/stores/showNotesToggle.spec.d.ts +1 -0
- package/lib/apps/content-selector/appTypes.d.ts +16 -0
- package/lib/apps/content-selector/components/Content/AllContent/List/expandFileGuard.spec.d.ts +1 -0
- package/lib/apps/notes/NotesApp.accountNotes.spec.d.ts +1 -0
- package/lib/apps/notes/NotesApp.speakerNotes.spec.d.ts +1 -0
- package/lib/apps/notes/composables/useAccountNotes.d.ts +14 -0
- package/lib/apps/notes/composables/useAccountNotes.spec.d.ts +1 -0
- package/lib/apps/notes/composables/useAccountOnlyNotesGate.d.ts +21 -0
- package/lib/apps/notes/composables/useAccountOnlyNotesGate.spec.d.ts +1 -0
- package/lib/apps/notes/composables/useActiveSlideNotes.d.ts +42 -0
- package/lib/apps/notes/composables/useActiveSlideNotes.spec.d.ts +1 -0
- package/lib/components/CFileAccessManagement/collaborationApi.d.ts +2 -0
- package/lib/components/CFilePanel/Details.spec.d.ts +1 -0
- package/lib/components/CFilePanel/KeyMessages.spec.d.ts +1 -0
- package/lib/components/CFilePanel/Summary.spec.d.ts +1 -0
- package/lib/components/CFileViewer/pdf/CFileViewer.pdf.util.d.ts +52 -0
- package/lib/components/CGlobalLoader/CGlobalLoaderMark.spec.d.ts +1 -0
- package/lib/components/CGlobalLoader/globalLoader.paths.d.ts +2 -0
- package/lib/components/CGlobalSearch/globalSearchTarget.util.d.ts +31 -0
- package/lib/components/CGlobalSearch/globalSearchTarget.util.test.d.ts +1 -0
- package/lib/components/CRichTextEditor/extensions/paragraphExtended.d.ts +7 -1
- package/lib/components/CRichTextEditor/extensions/paragraphExtended.test.d.ts +1 -0
- package/lib/components/canvases/CanvasesManagementBulk.use.d.ts +12 -2
- package/lib/components/masterCanvases/CMasterCanvasesManagement.use.d.ts +86 -0
- package/lib/components/masterCanvases/CMasterCanvasesManagement.use.spec.d.ts +1 -0
- package/lib/components/masterCanvases/CMasterCanvasesPendingShareCell.spec.d.ts +1 -0
- package/lib/components/masterCanvases/CMasterCanvasesSharedWithCell.use.d.ts +39 -0
- package/lib/components/masterCanvases/CMasterCanvasesSharedWithCell.use.spec.d.ts +1 -0
- package/lib/components/masterCanvases/createFromMaster.use.d.ts +255 -0
- package/lib/components/masterCanvases/createFromMaster.use.spec.d.ts +1 -0
- package/lib/components/masterCanvases/masterCanvasDocuments.use.d.ts +56 -0
- package/lib/components/masterCanvases/masterCanvasDocuments.use.spec.d.ts +1 -0
- package/lib/components/masterCanvases/masterCanvasShares.use.d.ts +103 -0
- package/lib/components/masterCanvases/masterCanvasShares.use.spec.d.ts +1 -0
- package/lib/components/masterCanvases/masterFromCanvas.use.d.ts +122 -0
- package/lib/components/masterCanvases/masterFromCanvas.use.spec.d.ts +1 -0
- package/lib/components/masterCanvases/masterFromTemplate.use.d.ts +50 -0
- package/lib/components/masterCanvases/masterFromTemplate.use.spec.d.ts +1 -0
- package/lib/components/savedCanvases/CSavedCanvasesManagement.use.d.ts +203 -0
- package/lib/components/savedCanvases/savedCanvasDocuments.use.d.ts +111 -0
- package/lib/components/savedCanvases/savedCanvasDocuments.use.spec.d.ts +1 -0
- package/lib/components/savedCanvases/smartFolders/SmartFolders.use.d.ts +4 -0
- package/lib/constants/cdp.const.d.ts +1 -0
- package/lib/constants/translationLanguages.const.d.ts +86 -8
- package/lib/constants/translationLanguages.const.spec.d.ts +1 -0
- package/lib/main.lib.d.ts +5 -0
- package/lib/sdk/api/HighLevelApi.d.ts +2 -0
- package/lib/sdk/api/modules/appsDb.d.ts +4 -4
- package/lib/sdk/api/modules/canvas.d.ts +12 -0
- package/lib/sdk/api/modules/ui/types.ui.d.ts +18 -0
- package/lib/sdk/interfaces.d.ts +31 -1
- package/lib/sdk/main.d.ts +38 -0
- package/lib/sdk/payload.types.d.ts +45 -0
- package/lib/sdk/platform.d.ts +27 -0
- package/lib/sdk/platform.spec.d.ts +1 -0
- package/lib/sdk/singleton-scope.spec.d.ts +1 -0
- package/lib/sdk/utils/httpFetch.spec.d.ts +1 -0
- package/lib/theme/canvasUiUnoPreset.test.d.ts +1 -0
- package/lib/types/app.d.ts +55 -1
- package/lib/types/canvases.d.ts +15 -0
- package/lib/types/launchDarkly.types.d.ts +8 -1
- package/lib/types/organizationSettings.types.d.ts +29 -0
- package/lib/util/aiSummary.d.ts +30 -0
- package/lib/util/aiSummary.spec.d.ts +1 -0
- package/lib/util/network.d.ts +35 -0
- package/package.json +1 -1
- package/types/openapi/models/Canvas.d.ts +1 -0
- package/types/openapi/models/CanvasCreate.d.ts +1 -0
- package/types/openapi/models/CanvasCreateRequest.d.ts +1 -0
- package/types/openapi/models/CanvasRecommendedFile.d.ts +1 -1
- package/types/openapi/models/CanvasRetrieve.d.ts +1 -0
- package/types/openapi/models/CanvasUpdate.d.ts +1 -0
- package/types/openapi/models/CanvasUpdateRequest.d.ts +1 -0
- package/types/openapi/models/DeletedCanvas.d.ts +1 -0
- package/types/openapi/models/File.d.ts +1 -1
- package/types/openapi/models/PatchedCanvasUpdateRequest.d.ts +1 -0
- package/types/openapi/models/SharedLink.d.ts +6 -0
- package/types/openapi/models/SharedLinkCreateRequest.d.ts +5 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { CanvasCreateRequest, CanvasRetrieve } from '../../../types/openapi';
|
|
2
|
+
/**
|
|
3
|
+
* Metadata for a MASTER created from an existing canvas ("Use as master").
|
|
4
|
+
*
|
|
5
|
+
* Strips the source deck's own bookkeeping — the async-download record, so the new master does not
|
|
6
|
+
* offer a download of the SOURCE rendered before it became a master, and `frozen_until`, so a
|
|
7
|
+
* brand-new master is not born read-only.
|
|
8
|
+
*
|
|
9
|
+
* Deliberately KEEPS `pitcher.completion_wizard`, which is what separates this from
|
|
10
|
+
* `buildChildMetadata`. A master is a finished source: block 3 lets the wizard state survive into
|
|
11
|
+
* `context` (`buildMasterContext` prunes identity only), so pruning the `metadata` twin here would
|
|
12
|
+
* desynchronise the two and make the list's Progress column disagree with the wizard itself.
|
|
13
|
+
* Reusing `buildChildMetadata` would have been the intuitive fix and the wrong one.
|
|
14
|
+
*/
|
|
15
|
+
export declare function buildMasterMetadata(sourceMetadata?: CanvasRetrieve['metadata'] | null): Record<string, unknown> | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* The create payload for "Use as master pitchdeck" (PIT-7488 block 3).
|
|
18
|
+
*
|
|
19
|
+
* Mirror image of `buildCreateFromMasterPayload`, and it lives beside it for the same reason
|
|
20
|
+
* `buildMasterMetadata` does: the two directions must be read together or they drift.
|
|
21
|
+
*
|
|
22
|
+
* **`owned_by_id` is absent, and that absence is the contract (PIT-8362).** Django declares the
|
|
23
|
+
* field `default=CreateOnlyDefault(CurrentUserDefault())`, so omitting it is precisely what makes
|
|
24
|
+
* the master belong to whoever promoted the deck — the only owner "My Master Pitchdecks" can
|
|
25
|
+
* meaningfully show, since it is a personal, RESTRICTED, owner-scoped tab that the promote flow
|
|
26
|
+
* navigates to on success. Sending the SOURCE deck's owner instead (block 3's original rule,
|
|
27
|
+
* reverted here) put a colleague's master in the promoter's tab, and `CanvasOwnedByFieldValidator`
|
|
28
|
+
* 400s that transfer outright for anyone but an org/instance admin. Do not reintroduce the field.
|
|
29
|
+
*
|
|
30
|
+
* The rest mirrors the source deck, minus what a master must not inherit:
|
|
31
|
+
* - `account: null` + `access_type: RESTRICTED` — masters are account-less internal sources rather
|
|
32
|
+
* than shared content (`resolve_account` nulls the account server-side anyway).
|
|
33
|
+
* - `context` — identity is stripped so every deck built from this master renders the PRESENTING
|
|
34
|
+
* rep in `{{user.name}}`, not the promoter. See `buildMasterContext`.
|
|
35
|
+
* - `metadata` — drops the source's async-download record (its Actions cell would otherwise offer a
|
|
36
|
+
* completed download of the deck as it was BEFORE becoming a master) and `pitcher.frozen_until`
|
|
37
|
+
* (a brand-new master born read-only). Deliberately NOT `buildChildMetadata`: that also drops the
|
|
38
|
+
* completion wizard, which a master keeps, and `buildMasterContext` keeps its `context` twin —
|
|
39
|
+
* the two must agree.
|
|
40
|
+
*
|
|
41
|
+
* - `is_draft: false` — forced, NOT inherited (PIT-8360). Masters have no draft state since the
|
|
42
|
+
* Ready column was dropped from the master tab (validation item #44), so nothing can clear the
|
|
43
|
+
* flag afterwards: a deck promoted while still a draft would produce a permanently-draft master.
|
|
44
|
+
* This keeps the promote path agreeing with the other three master-creation paths in
|
|
45
|
+
* `MasterCanvasesView`, which force it too, rather than leaving `is_draft` set on masters with
|
|
46
|
+
* nothing that sets it FOR them, waiting to bite whoever next writes a filter against it.
|
|
47
|
+
*/
|
|
48
|
+
export declare function buildMasterFromCanvasPayload(opts: {
|
|
49
|
+
source: CanvasRetrieve;
|
|
50
|
+
name: string;
|
|
51
|
+
}): Omit<CanvasCreateRequest, 'instance_id'>;
|
|
52
|
+
/**
|
|
53
|
+
* Drops the SOURCE's completion-wizard progress from a context that is otherwise copied verbatim.
|
|
54
|
+
*
|
|
55
|
+
* For the plain Duplicate action, which — unlike create-from-master — legitimately keeps the
|
|
56
|
+
* source's identity: the copy belongs to the same person, for the same account, so stripping
|
|
57
|
+
* `user`/`account` there would blank a deck the rep expects to be a faithful duplicate. The wizard
|
|
58
|
+
* state is different: `isCompletionWizardShowing` is `wizardSteps.length > 0 &&
|
|
59
|
+
* !context.pitcher.completion_wizard.completed`, so inheriting `completed: true` makes the copy
|
|
60
|
+
* open straight into the editor with the wizard silently skipped.
|
|
61
|
+
*
|
|
62
|
+
* Returns `undefined` when nothing survives, so the payload omits the field rather than writing an
|
|
63
|
+
* empty object over the server's default.
|
|
64
|
+
*/
|
|
65
|
+
export declare function withoutWizardState(context?: CanvasRetrieve['context'] | null): Record<string, unknown> | undefined;
|
|
66
|
+
/**
|
|
67
|
+
* Context for a DUPLICATE (PIT-7488, peer review round 2).
|
|
68
|
+
*
|
|
69
|
+
* A pitchdeck copy strips the wizard state outright — see `withoutWizardState`. A MASTER copy cannot
|
|
70
|
+
* do either extreme:
|
|
71
|
+
*
|
|
72
|
+
* - Stripping it outright makes the copy look un-run, so the wizard reopens as a fresh `create`
|
|
73
|
+
* seeded from the ADMIN GROUP CONFIG and completing it writes that seed over the curated slides
|
|
74
|
+
* the rep just duplicated.
|
|
75
|
+
* - Keeping it verbatim inherits `completed: true`, which is the switch `isCompletionWizardShowing`
|
|
76
|
+
* reads — the copy then opens straight into the editor and never offers the wizard at all, while
|
|
77
|
+
* its Progress chip (from `metadata`) still says In progress. That desync is the whole reason
|
|
78
|
+
* `withoutWizardState` exists.
|
|
79
|
+
*
|
|
80
|
+
* So a master copy is re-marked as what it actually is: a deck that HAS been through the wizard but
|
|
81
|
+
* is not finished. `react_saved` is exactly that state — `resolveWizardFlow` reads it as `edit` (so
|
|
82
|
+
* the copy restores its OWN content), while `completed` stays falsy so the wizard still shows and
|
|
83
|
+
* the chip still reads In progress, agreeing with `buildChildMetadata`.
|
|
84
|
+
*
|
|
85
|
+
* The marker is added only when the source carries a RESTORABLE artifact — see
|
|
86
|
+
* `hasRestorableArtifact`, which is the sole gate on this path because the edit flow has no
|
|
87
|
+
* downstream `fromArtifact` check. A master with nothing to restore must not claim `react_saved`, or
|
|
88
|
+
* the edit flow restores from an artifact that is not there and reopens the empty-`edit` bug (Q9).
|
|
89
|
+
* `initial_section_list_data` is dropped either way, so the copy re-seeds its candidate baseline
|
|
90
|
+
* from its own content rather than re-offering slides the source deliberately dropped.
|
|
91
|
+
*/
|
|
92
|
+
export declare function buildDuplicateContext(context?: CanvasRetrieve['context'] | null, opts?: {
|
|
93
|
+
isMaster?: boolean;
|
|
94
|
+
}): Record<string, unknown> | undefined;
|
|
95
|
+
/**
|
|
96
|
+
* Freezing a master says "this source is locked", not "every deck built from it is locked" — and a
|
|
97
|
+
* brand new deck the rep cannot edit is unusable.
|
|
98
|
+
*/
|
|
99
|
+
export declare function buildChildMetadata(masterMetadata?: CanvasRetrieve['metadata'] | null): Record<string, unknown> | undefined;
|
|
100
|
+
/**
|
|
101
|
+
* The child's context: the master's configuration plus this session's identity.
|
|
102
|
+
*
|
|
103
|
+
* Identity is **stripped and re-added** rather than merged over, and that is the load-bearing
|
|
104
|
+
* detail. `getDefaultCrmShape` runs its result through `removeEmptyValues`, so on a non-CRM org
|
|
105
|
+
* (or with no live call) it emits no `account` and no `meeting` at all — a plain merge would then
|
|
106
|
+
* leave whatever the master carried, which is the wrong customer's name on the new deck. Stripping
|
|
107
|
+
* first means the child's identity keys can only ever come from the current session.
|
|
108
|
+
*
|
|
109
|
+
* It cannot rely on block 3 having cleaned the master either: `buildMasterContext` runs once, at
|
|
110
|
+
* creation, and opening a master in the builder afterwards deep-merges live `Account` /
|
|
111
|
+
* `salesforce_data` / `pitcher_data` back into its context. Every master is therefore re-poisonable
|
|
112
|
+
* between creation and use, and this is the last gate before a customer sees the result.
|
|
113
|
+
*
|
|
114
|
+
* `accountName` overrides whatever the CRM shape resolved because `getDefaultCrmShape` prefers the
|
|
115
|
+
* LIVE call's selected account (`call.selectedAccount.Name ?? accountName`). The account chosen in
|
|
116
|
+
* the dialog is the deck's account, so a rep prepping a deck for Beta during a call with Acme must
|
|
117
|
+
* not get Acme's name interpolated into it.
|
|
118
|
+
*
|
|
119
|
+
* The wizard artifact's OWN identity goes too (`withoutWizardIdentity`): the top-level key list
|
|
120
|
+
* never reached `wizard.subtitle`, so the promoter's customer rode onto the child's cover slide via
|
|
121
|
+
* `{{wizard.subtitle.label}}` (PIT-8399). Masters are re-poisonable after creation, so this pass
|
|
122
|
+
* has to run here as well as in `buildMasterContext`.
|
|
123
|
+
*
|
|
124
|
+
* The master's wizard PROGRESS is replaced rather than merely dropped (`pitcher.completion_wizard`,
|
|
125
|
+
* see `WIZARD_STATE_PITCHER_KEY`). Dropping it is what lets the completion wizard run on the new
|
|
126
|
+
* deck — on a `enable_react_completion_wizard` instance, the `pitchdeck-wizard-ng` app — but a deck
|
|
127
|
+
* with NO wizard state at all also reads as never-curated, and the wizard's create flow then
|
|
128
|
+
* reseeds its agenda from the ADMIN GROUP CONFIG, silently dropping every custom agenda point the
|
|
129
|
+
* master carried (PIT-8399, the reported defect). So the progress is reset to the one fact that
|
|
130
|
+
* survives: `from_master`, which says "this deck's content was copied from a curated source, so its
|
|
131
|
+
* own artifact is the authoritative agenda". `resolveWizardAgendaSource` reads it.
|
|
132
|
+
*
|
|
133
|
+
* `from_master` deliberately sets NONE of `completed` / `edited` / `react_saved`, so
|
|
134
|
+
* `resolveWizardFlow` still answers `'create'`: the deck really is being created, the editor still
|
|
135
|
+
* says so, and identity stays with the create flow rather than being restored off the master. That
|
|
136
|
+
* is what separates this from `buildDuplicateContext`, which marks a master COPY `react_saved` and
|
|
137
|
+
* accepts the `'edit'` flow because a duplicate legitimately IS the source. Do not unify the two.
|
|
138
|
+
*
|
|
139
|
+
* **Knock-on the marker's PRESENCE has** (review): `App.vue` computes
|
|
140
|
+
* `hasWizardState: isCompletionWizardEnabled || !!context.pitcher.completion_wizard` as a
|
|
141
|
+
* deliberate proxy for the window before the app roster loads, so a from-master deck now satisfies
|
|
142
|
+
* it from creation onward where it previously had no such key. With
|
|
143
|
+
* `enable_edit_opens_completion_wizard` on, `isWizardOwnedEditSurface` therefore holds the surface
|
|
144
|
+
* for such a deck immediately — correct whenever the deck has wizard steps, which is every normal
|
|
145
|
+
* case, and the marker is only written when the master carried an artifact, which means the wizard
|
|
146
|
+
* had already run on it. The sharp edge is a deck with an artifact and ZERO steps (its SectionList
|
|
147
|
+
* node deleted in the raw builder after the wizard ran): it is demoted out of edit mode with no
|
|
148
|
+
* wizard to open. That edge PREDATES this change — every deck that has ever run the wizard carries
|
|
149
|
+
* the same key — and this only adds one more way to reach it, so it is accepted rather than worked
|
|
150
|
+
* around here. Do not "optimise" the marker away on the assumption it is inert.
|
|
151
|
+
*
|
|
152
|
+
* Written only when the copied context actually still CARRIES an artifact (`context.wizard`) — not
|
|
153
|
+
* when the master merely had wizard PROGRESS. Those two come apart in both directions and both are
|
|
154
|
+
* wrong: `ensureWizardInitialized` writes `pitcher.completion_wizard` when the wizard OPENS, so a
|
|
155
|
+
* master whose wizard was opened and cancelled has progress and no artifact (5 such masters live on
|
|
156
|
+
* test-eu), while a master with an artifact and no progress key would be left unfixed. Claiming an
|
|
157
|
+
* artifact that is not there reopens the empty-`edit` bug from the other side (PIT-7488 QA Q9): the
|
|
158
|
+
* restore finds no skeletons and parks every ref on one unnamed point.
|
|
159
|
+
*
|
|
160
|
+
* Returns `undefined` rather than `{}` so the create payload omits the field instead of writing an
|
|
161
|
+
* empty object over the server's default.
|
|
162
|
+
*/
|
|
163
|
+
export declare function buildContextFromMaster(opts: {
|
|
164
|
+
masterContext?: CanvasRetrieve['context'] | null;
|
|
165
|
+
/** `getDefaultCrmShape({ accountName, myUser })` — passed in because it needs Vue injection. */
|
|
166
|
+
crmShape?: Record<string, unknown> | null;
|
|
167
|
+
/** The account chosen in the dialog, already privacy-mode-adjusted (blank name = store no name). */
|
|
168
|
+
account?: {
|
|
169
|
+
id: string;
|
|
170
|
+
name?: string;
|
|
171
|
+
} | null;
|
|
172
|
+
}): Record<string, unknown> | undefined;
|
|
173
|
+
/**
|
|
174
|
+
* The create payload for a pitchdeck built from a master.
|
|
175
|
+
*
|
|
176
|
+
* `is_master: false` is spelled out rather than left to the spread: the fetched master carries
|
|
177
|
+
* `is_master: true`, and the flow also runs FROM the master tab, whose own `creator` exists to make
|
|
178
|
+
* masters. The child is an ordinary deck — it appears on the Pitchdecks tab, it has an account, and
|
|
179
|
+
* it is the thing the rep presents.
|
|
180
|
+
*
|
|
181
|
+
* `is_draft: false` matches the platform's other rep-facing creation flows (the create wizard posts
|
|
182
|
+
* `is_draft: false`). Inheriting the master's flag would silently mark customer-facing decks as
|
|
183
|
+
* drafts whenever the master was left in draft — a master's own status is bookkeeping for a source
|
|
184
|
+
* artifact and says nothing about the deck made from it.
|
|
185
|
+
*
|
|
186
|
+
* `access_type` is deliberately absent: it rides along from the master via the spread, and the host
|
|
187
|
+
* `creator` forces the value it wants (RESTRICTED for rep-created decks).
|
|
188
|
+
*/
|
|
189
|
+
export declare function buildCreateFromMasterPayload(opts: {
|
|
190
|
+
master: CanvasRetrieve;
|
|
191
|
+
name: string;
|
|
192
|
+
/** Already privacy-mode-adjusted by the caller (`enable_crm_privacy_mode` blanks the name). */
|
|
193
|
+
account?: {
|
|
194
|
+
id: string;
|
|
195
|
+
name?: string;
|
|
196
|
+
} | null;
|
|
197
|
+
crmShape?: Record<string, unknown> | null;
|
|
198
|
+
}): Omit<CanvasCreateRequest, 'instance_id'>;
|
|
199
|
+
/**
|
|
200
|
+
* Whether the dialog must collect an account before it can create.
|
|
201
|
+
*
|
|
202
|
+
* Follows the platform setting rather than the prototype's always-required Account field: on a
|
|
203
|
+
* non-CRM org there are no accounts to offer, so requiring one would make the whole feature
|
|
204
|
+
* unreachable. Same rule the create modal and the duplicate flow already apply.
|
|
205
|
+
*/
|
|
206
|
+
export declare function shouldRequireAccountOnCreateFromMaster(opts: {
|
|
207
|
+
isCRM?: boolean;
|
|
208
|
+
requireAccountOnCanvasCreation?: boolean;
|
|
209
|
+
}): boolean;
|
|
210
|
+
type MasterOption = Pick<CanvasRetrieve, 'id' | 'name'>;
|
|
211
|
+
/**
|
|
212
|
+
* Guarantees the master a row action was fired from is in the offered list.
|
|
213
|
+
*
|
|
214
|
+
* Since PIT-8360 dropped the picker's `is_draft: false` fetch filter the two lists agree, so this is
|
|
215
|
+
* now a guard rather than the everyday path: it still covers a master created or renamed after the
|
|
216
|
+
* list was fetched, and the `fetchAll` truncation at `MAX_FETCH_LIMIT`. Without it the rep could
|
|
217
|
+
* click a master's "Use to create pitchdeck" and land in a picker that neither preselects nor offers
|
|
218
|
+
* it. Sibling of `buildMasterPickerOptions`, which applies the same rule to the Vue dialog's option
|
|
219
|
+
* shape.
|
|
220
|
+
*/
|
|
221
|
+
export declare function withPreselectedMaster<T extends {
|
|
222
|
+
id: string;
|
|
223
|
+
}>(masters: T[], preselected?: T | null): T[];
|
|
224
|
+
/**
|
|
225
|
+
* Options for the dialog's master picker.
|
|
226
|
+
*
|
|
227
|
+
* The picker lists every master since PIT-8360 dropped the fetch's `is_draft: false` filter — with
|
|
228
|
+
* the master tab's Ready column gone, nothing can clear the flag, so filtering would permanently
|
|
229
|
+
* hide masters already stored as drafts.
|
|
230
|
+
*
|
|
231
|
+
* **No draft marker on the labels** (PR review). It used to suffix "(Draft)", which was already
|
|
232
|
+
* unreachable — the fetch narrows to `id,name,tags,modified_at`, so every option arrived with
|
|
233
|
+
* `is_draft === undefined`. Restoring it via the `fields` list would have been the wrong repair:
|
|
234
|
+
* masters have no draft state to advertise any more, and a badge on a subset of them describes a
|
|
235
|
+
* distinction nothing in the product can act on. Dropping it also removes the hook a future reader
|
|
236
|
+
* would most likely rebuild a filter around.
|
|
237
|
+
*
|
|
238
|
+
* The preselected master is still injected — see `withPreselectedMaster` for the cases that still
|
|
239
|
+
* reaches.
|
|
240
|
+
*/
|
|
241
|
+
export declare function buildMasterPickerOptions(opts: {
|
|
242
|
+
masters: MasterOption[];
|
|
243
|
+
preselected?: MasterOption | null;
|
|
244
|
+
}): {
|
|
245
|
+
label: string;
|
|
246
|
+
value: string;
|
|
247
|
+
}[];
|
|
248
|
+
/** Guards the submit button. A blank name would create a nameless row in the rep's own list. */
|
|
249
|
+
export declare function canSubmitCreateFromMaster(opts: {
|
|
250
|
+
name?: string | null;
|
|
251
|
+
masterId?: string | null;
|
|
252
|
+
isAccountRequired?: boolean;
|
|
253
|
+
accountId?: string | null;
|
|
254
|
+
}): boolean;
|
|
255
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PIT-7488 block 7 — pure logic behind the Documents column.
|
|
3
|
+
*
|
|
4
|
+
* Display only. The decision of WHICH masters to render lives server-side, in next-core's
|
|
5
|
+
* `selectCanvasIdsNeedingRender`: the bulk read already computes `is_stale` in SQL, so keeping the
|
|
6
|
+
* policy next to it avoids two copies of the same rule drifting apart in two languages.
|
|
7
|
+
*
|
|
8
|
+
* Kept free of Vue so the state machine is testable on its own.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Mirrors next-core's `MasterDocumentsState`. Duplicated deliberately — next-core types are not
|
|
12
|
+
* published to canvas-ui, and canvas-ui must not import from next-core.
|
|
13
|
+
*/
|
|
14
|
+
export interface MasterDocumentsState {
|
|
15
|
+
status: 'pending' | 'rendering' | 'ready' | 'failed';
|
|
16
|
+
pdf_url: string | null;
|
|
17
|
+
pptx_url: string | null;
|
|
18
|
+
rendered_at: string | null;
|
|
19
|
+
is_stale: boolean;
|
|
20
|
+
attempts: number;
|
|
21
|
+
error: string | null;
|
|
22
|
+
}
|
|
23
|
+
export type DocumentsCellState =
|
|
24
|
+
/** No documents row at all — this master has never been rendered. */
|
|
25
|
+
'none'
|
|
26
|
+
/** A render is queued or running. */
|
|
27
|
+
| 'rendering'
|
|
28
|
+
/** Current documents are available. */
|
|
29
|
+
| 'ready'
|
|
30
|
+
/** Documents are available but out of date; a fresh render is coming. */
|
|
31
|
+
| 'ready_refreshing'
|
|
32
|
+
/** The last render failed. */
|
|
33
|
+
| 'failed';
|
|
34
|
+
/**
|
|
35
|
+
* Must match next-core's `MAX_RENDER_ATTEMPTS`. A client offering a fourth retry would render a
|
|
36
|
+
* button the server refuses to act on.
|
|
37
|
+
*/
|
|
38
|
+
export declare const MAX_RENDER_ATTEMPTS = 3;
|
|
39
|
+
/**
|
|
40
|
+
* Maps a documents row onto what the cell should show.
|
|
41
|
+
*
|
|
42
|
+
* The `ready` + stale case deliberately keeps the existing links rather than reverting to a spinner:
|
|
43
|
+
* a slightly out-of-date document a rep can open beats a dead control. Only a genuinely absent
|
|
44
|
+
* artifact shows progress.
|
|
45
|
+
*/
|
|
46
|
+
export declare function resolveDocumentsCellState(state: MasterDocumentsState | undefined | null): DocumentsCellState;
|
|
47
|
+
/**
|
|
48
|
+
* Whether to offer Retry. Only for a failed row, and only while the server would still act on it.
|
|
49
|
+
*/
|
|
50
|
+
export declare function canRetryDocuments(state: MasterDocumentsState | undefined | null): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Whether the cell has anything openable. Drives the disabled state of each link independently —
|
|
53
|
+
* the two artifacts have different lifetimes (the PPTX object expires days before the PDF's URL
|
|
54
|
+
* mapping does), so one can be present while the other is not.
|
|
55
|
+
*/
|
|
56
|
+
export declare function hasOpenableDocument(state: MasterDocumentsState | undefined | null, format: 'pdf' | 'pptx'): boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { MiniUser } from '../../../types/openapi';
|
|
2
|
+
/** Exactly `Collaboration['user']` and the "Sent to" cell's prop element (PIT-8421; was "Shared with"). */
|
|
3
|
+
export type MasterCanvasShareUser = MiniUser;
|
|
4
|
+
/**
|
|
5
|
+
* PIT-7488 block 5 — may the current user CHANGE this master canvas (Edit / Delete / Freeze)?
|
|
6
|
+
*
|
|
7
|
+
* The owner, **or an instance admin/editor**. That second arm mirrors Django's write gate
|
|
8
|
+
* exactly — `CanvasPermission.has_object_permission` returns True for
|
|
9
|
+
* `is_instance_admin or is_instance_editor or obj.is_owner(user)` — and it matters because the
|
|
10
|
+
* master list is not "only mine" for those roles: admins and editors route through
|
|
11
|
+
* `filter_by_admin_access`, so they see every master in the instance. Gating on ownership alone
|
|
12
|
+
* made the UI STRICTER than the API and silently removed Edit/Delete/Freeze from every master an
|
|
13
|
+
* admin did not personally own.
|
|
14
|
+
*
|
|
15
|
+
* A share recipient never reaches this gate on the SENDER's master: accepting an invitation mints
|
|
16
|
+
* a copy they own, so they meet the ownership arm on their own row and the sender's original is
|
|
17
|
+
* not in their list at all. That is why ng-rest-api#1688's read gates were reverted (#1689) —
|
|
18
|
+
* under invitation semantics there is no "viewing someone else's master" state to widen for.
|
|
19
|
+
*
|
|
20
|
+
* Fails closed, and deliberately not a bare `ownerId === myUserId`: that returns true when both
|
|
21
|
+
* are `undefined`, which would hand a recipient full control of any master whose owner failed to
|
|
22
|
+
* serialise.
|
|
23
|
+
*/
|
|
24
|
+
export declare function canManageMasterCanvas(opts: {
|
|
25
|
+
ownerId?: number | null;
|
|
26
|
+
myUserId?: number | null;
|
|
27
|
+
isAdminOrEditor?: boolean;
|
|
28
|
+
}): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* PIT-7488 block 5 — may the current user SHARE this master canvas?
|
|
31
|
+
*
|
|
32
|
+
* The owner ONLY — deliberately narrower than `canManageMasterCanvas`. The next-core route is
|
|
33
|
+
* owner-gated (`master.ownedById !== user.id` → 403), so offering "Share with" to an admin on
|
|
34
|
+
* someone else's master would surface an action the API refuses. Sharing is the owner's call
|
|
35
|
+
* about their own material, not a moderation capability.
|
|
36
|
+
*/
|
|
37
|
+
export declare function canShareMasterCanvas(opts: {
|
|
38
|
+
ownerId?: number | null;
|
|
39
|
+
myUserId?: number | null;
|
|
40
|
+
}): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Per-user outcome of a send, mirroring next-core's `InvitationResult`.
|
|
43
|
+
*
|
|
44
|
+
* `already_invited` is benign (the UNIQUE key makes a repeat a no-op); `not_a_member` means the
|
|
45
|
+
* picker offered someone the server then refused, which the sender needs told.
|
|
46
|
+
*/
|
|
47
|
+
export type MasterShareInvitationStatus = 'invited' | 'already_invited' | 'not_a_member' | 'self';
|
|
48
|
+
export interface MasterShareInvitationResult {
|
|
49
|
+
user_id: number;
|
|
50
|
+
status: MasterShareInvitationStatus;
|
|
51
|
+
}
|
|
52
|
+
/** One pending invitation, as the recipient's list renders it. */
|
|
53
|
+
export interface PendingMasterShare {
|
|
54
|
+
share_id: number;
|
|
55
|
+
canvas_id: string;
|
|
56
|
+
canvas_name: string;
|
|
57
|
+
instance_id: string;
|
|
58
|
+
shared_by: MiniUser;
|
|
59
|
+
created_at: string;
|
|
60
|
+
}
|
|
61
|
+
export declare function pendingRowId(shareId: number): string;
|
|
62
|
+
export declare function isPendingRowId(id?: string | number | null): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* The share id behind a pending row id, or `null` if it is not one.
|
|
65
|
+
*
|
|
66
|
+
* Digits only, deliberately: `Number('')` is `0` and `Number.isInteger(0)` is true, so a bare
|
|
67
|
+
* `'pending:'` used to parse to share id 0 — and `Number` also accepts whitespace, `0x` and
|
|
68
|
+
* exponent forms. Share ids are a positive `serial`, so anything else is a bug upstream and must
|
|
69
|
+
* read as "not a pending row" rather than as row 0.
|
|
70
|
+
*/
|
|
71
|
+
export declare function pendingShareIdFromRowId(id?: string | number | null): number | null;
|
|
72
|
+
/**
|
|
73
|
+
* Shapes a pending invitation as a table row.
|
|
74
|
+
*
|
|
75
|
+
* `owned_by` carries the SHARER, which is what lets the existing Owner column render "who sent
|
|
76
|
+
* this" with no special-casing — for an invitation the only person worth naming is the sender.
|
|
77
|
+
* `is_master` keeps it consistent with the tab it appears on; every other canvas field is left
|
|
78
|
+
* absent so the remaining cells render empty rather than inventing state the invitation has not
|
|
79
|
+
* got (no tags, no progress, no modified date, no ready/draft status).
|
|
80
|
+
*/
|
|
81
|
+
export interface PendingMasterShareRow {
|
|
82
|
+
id: string;
|
|
83
|
+
name: string;
|
|
84
|
+
owned_by: MiniUser;
|
|
85
|
+
is_master: true;
|
|
86
|
+
created_at: string;
|
|
87
|
+
modified_at: string;
|
|
88
|
+
__pendingShare: PendingMasterShare;
|
|
89
|
+
}
|
|
90
|
+
export declare function toPendingRow(share: PendingMasterShare): PendingMasterShareRow;
|
|
91
|
+
/**
|
|
92
|
+
* Pending invitations first, then the user's own masters.
|
|
93
|
+
*
|
|
94
|
+
* Prepended rather than sorted in: an invitation is an inbox item needing a decision, and the
|
|
95
|
+
* table's sort applies to the fetched page — a pending row sorted by `modified_at` would sink
|
|
96
|
+
* below the fold on a rep with many masters and never be seen.
|
|
97
|
+
*
|
|
98
|
+
* Returns the list unchanged when there is nothing pending, so the non-master variants and the
|
|
99
|
+
* common case allocate nothing.
|
|
100
|
+
*/
|
|
101
|
+
export declare function mergePendingRows<T extends {
|
|
102
|
+
id: string;
|
|
103
|
+
}>(rows: T[], pending: PendingMasterShare[]): (T | PendingMasterShareRow)[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { CanvasRetrieve } from '../../../types/openapi';
|
|
2
|
+
/**
|
|
3
|
+
* PIT-7488 block 3 — turning an existing pitchdeck into a master.
|
|
4
|
+
*
|
|
5
|
+
* The deck is DUPLICATED, never mutated: the rep keeps the deck they were presenting, and a new
|
|
6
|
+
* `is_master` copy appears on the master tab.
|
|
7
|
+
*
|
|
8
|
+
* **The promoter owns the master, always — so the create payload must OMIT `owned_by_id`**
|
|
9
|
+
* (PIT-8362; the payload itself is `buildMasterFromCanvasPayload`, beside its block-4 mirror in
|
|
10
|
+
* `createFromMaster.use`). Django declares that field
|
|
11
|
+
* `default=CreateOnlyDefault(CurrentUserDefault())`, so leaving it out is precisely what assigns
|
|
12
|
+
* the caller; sending anything is a transfer, which `CanvasOwnedByFieldValidator` 400s for anyone
|
|
13
|
+
* but an org/instance admin.
|
|
14
|
+
*
|
|
15
|
+
* This reverses block 3's original rule, so read the history before "fixing" it back. That rule
|
|
16
|
+
* kept the SOURCE deck's owner, on the reasoning that a master should stay attributed to whoever
|
|
17
|
+
* built the deck. Vontobel's validation rejected it: "My Master Pitchdecks" is a personal,
|
|
18
|
+
* RESTRICTED, owner-scoped tab that the promote flow navigates to on success, so a master owned by
|
|
19
|
+
* someone else lands in a tab it does not belong in — invisible to a rep (`filter_by_member_access`
|
|
20
|
+
* is `owned_by=me OR public`), and attributed to a stranger for an admin, who sees the whole
|
|
21
|
+
* instance anyway.
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* Context keys that must NOT survive onto a master.
|
|
25
|
+
*
|
|
26
|
+
* A master is a reusable *source*, so anything baked into its context propagates to every deck
|
|
27
|
+
* built from it later — `{{user.name}}` would render whoever ran "Use as master" rather than the
|
|
28
|
+
* rep actually presenting. That is the wrong-identity-on-a-copy defect class, one step upstream, and it is the
|
|
29
|
+
* same reasoning that keeps the master download path from PATCHing `context`.
|
|
30
|
+
*
|
|
31
|
+
* This is a DENYLIST on purpose. `pitcher_canvas.context` is a free-form client-written
|
|
32
|
+
* JSONField that also carries customer-specific keys, so an allowlist would silently drop those,
|
|
33
|
+
* and would drop any config key added later — which is exactly how PIT-7473 broke the pitchdeck
|
|
34
|
+
* wizard. Failing this way round means a *new* identity key leaks until it is listed here, which
|
|
35
|
+
* shows up as a visibly wrong name on a deck and is a one-line fix.
|
|
36
|
+
*/
|
|
37
|
+
export declare const MASTER_CONTEXT_IDENTITY_KEYS: readonly ["user", "today", "Account", "salesforce_data", "Meeting", "pitcher_data", "analytics", "account", "meeting", "SelectedAccount"];
|
|
38
|
+
/**
|
|
39
|
+
* Top-level context key FAMILIES that are identity, matched by prefix rather than by name.
|
|
40
|
+
*
|
|
41
|
+
* `ctx_overwrite_<slideIndex>` is written by the `react-salesforce-admin` populator
|
|
42
|
+
* (`public/populator.html:509-525`) and holds BOTH a `pitcher_data` and a `salesforce_data` copy of
|
|
43
|
+
* a matched customer record, scoped to one slide — the same two namespaces
|
|
44
|
+
* `MASTER_CONTEXT_IDENTITY_KEYS` already strips at the top level, one nesting deeper and therefore
|
|
45
|
+
* invisible to a literal-key `omit`. A master promoted from a rep's deck carried the promoter's
|
|
46
|
+
* customer (client name, ids, figures) onto every deck built from it, per slide (PIT-8399 review).
|
|
47
|
+
*
|
|
48
|
+
* The key count is unbounded (one per matched slide) and the names embed a component id, so this
|
|
49
|
+
* cannot be expressed as literals — hence a prefix list.
|
|
50
|
+
*
|
|
51
|
+
* **Not to be confused with `context.wizard.overwrite_<sectionListId>_<n>`**, which is the WIZARD's
|
|
52
|
+
* positional slide override (`buildStepUpdate`) and is curation a master must keep. The two are
|
|
53
|
+
* cleanly separable: the populator's lives at the top level, the wizard's inside `wizard`.
|
|
54
|
+
*/
|
|
55
|
+
export declare const MASTER_CONTEXT_IDENTITY_KEY_PREFIXES: readonly ["ctx_overwrite_"];
|
|
56
|
+
/** Drops every top-level key matching one of the identity prefixes above. Non-mutating. */
|
|
57
|
+
export declare function withoutPrefixedIdentityKeys(context: Record<string, unknown>): Record<string, unknown>;
|
|
58
|
+
/**
|
|
59
|
+
* Whether a context carries a wizard artifact the restore could actually rebuild POINTS from.
|
|
60
|
+
*
|
|
61
|
+
* Mirrors the two — and only two — skeleton sources of the app's restore walk: the enrichment's
|
|
62
|
+
* per-point spans (`react_wizard.points`) and the vanilla group selections
|
|
63
|
+
* (`group_selections_<sectionListId>`). BOTH must be non-empty arrays: `for (const sel of
|
|
64
|
+
* selections)` and the enrichment loop each build zero skeletons from `[]`, so a present-but-empty
|
|
65
|
+
* key describes nothing. Checking only the selections KEY made this looser than the app's
|
|
66
|
+
* `fromArtifact` (which uses `selections.length > 0`) — and on the duplicate path, which has no
|
|
67
|
+
* second gate, looser means unguarded. Everything else under `wizard` is answers, cover picks or
|
|
68
|
+
* admin config: the live repro deck carries `selected_assets`, `selected_asset_url`, `selected_file`
|
|
69
|
+
* and `title`, and the populator reads a `wizard.dataIndexMap` — none of which can rebuild a point.
|
|
70
|
+
*
|
|
71
|
+
* So `!!context.wizard` is NOT the same question, and the difference bites hardest in
|
|
72
|
+
* `buildDuplicateContext`: its marker selects `react_saved`, which resolves to the EDIT flow, whose
|
|
73
|
+
* restore is deliberately ungated on the app's own `fromArtifact` check (a vanilla deck legitimately
|
|
74
|
+
* restores by attribution). A master whose `wizard` held only cover picks therefore opened with an
|
|
75
|
+
* agenda rebuilt by attribution — the PIT-7488 QA Q9 outcome — with nothing downstream to catch it
|
|
76
|
+
* (peer review F2).
|
|
77
|
+
*
|
|
78
|
+
* Deliberately WEAKER than the app's `fromArtifact`, which also requires the enrichment spans to
|
|
79
|
+
* partition the node's ref list. The host has no refs to hand here, so it answers "are there
|
|
80
|
+
* skeleton sources at all"; the app makes the precise call. That is the two-gate design, and it is
|
|
81
|
+
* why the two predicates are allowed to differ.
|
|
82
|
+
*/
|
|
83
|
+
export declare function hasRestorableArtifact(context?: Record<string, unknown> | null): boolean;
|
|
84
|
+
/** Suffix appended to the source deck's name to seed the dialog. Matches the prototype. */
|
|
85
|
+
export declare const MASTER_NAME_SUFFIX = "(master)";
|
|
86
|
+
/**
|
|
87
|
+
* Strips the identity above out of `context.wizard`, leaving the curation intact.
|
|
88
|
+
*
|
|
89
|
+
* Non-mutating — `omit` returns a new object at every level this touches, so the source master's
|
|
90
|
+
* own context is never altered. A `wizard` (or a `react_wizard`) left with nothing in it is
|
|
91
|
+
* dropped rather than kept as `{}`, the same rule `withoutPitcherKeys` follows for
|
|
92
|
+
* `context.pitcher`. A context carrying no `wizard` at all is returned untouched, which is what
|
|
93
|
+
* makes this safe to compose onto every master-context builder.
|
|
94
|
+
*
|
|
95
|
+
* Deliberately does NOT touch `react_wizard.points` / `version`: `restoreAgenda` validates the
|
|
96
|
+
* enrichment by checking that its per-point `slide_count`s partition the node's ref list exactly,
|
|
97
|
+
* so pruning identity here cannot invalidate the artifact the agenda is rebuilt from.
|
|
98
|
+
*/
|
|
99
|
+
export declare function withoutWizardIdentity(context: Record<string, unknown>): Record<string, unknown>;
|
|
100
|
+
/**
|
|
101
|
+
* The context to store on the new master: everything the source deck carried, minus identity.
|
|
102
|
+
*
|
|
103
|
+
* Identity is pruned at all THREE places it hides — the top-level keys above, the prefixed
|
|
104
|
+
* `ctx_overwrite_*` families (`withoutPrefixedIdentityKeys`), and the wizard artifact's own
|
|
105
|
+
* (`withoutWizardIdentity`). A master promoted from a deck the rep had run the
|
|
106
|
+
* wizard on carried the promoter's customer in `wizard.subtitle`, which deck templates render on
|
|
107
|
+
* the cover slide, so the top-level pass alone was not enough (PIT-8399).
|
|
108
|
+
*
|
|
109
|
+
* Deliberately NOT gated on `copy_context_to_canvas`. That flag exists because copying context
|
|
110
|
+
* wholesale was risky, and the risk was the identity keys stripped here — gating on it would
|
|
111
|
+
* hand flag-off instances a master with no `section_list_config`, reintroducing PIT-7473 for
|
|
112
|
+
* every deck built from it.
|
|
113
|
+
*
|
|
114
|
+
* Returns `undefined` rather than `{}` when nothing survives, so the create payload omits the
|
|
115
|
+
* field instead of writing an empty object over the server's default.
|
|
116
|
+
*/
|
|
117
|
+
export declare function buildMasterContext(context?: CanvasRetrieve['context'] | null): Record<string, unknown> | undefined;
|
|
118
|
+
/**
|
|
119
|
+
* Seeds the dialog's name field. The rep can overwrite it; the suffix only exists so the master
|
|
120
|
+
* is distinguishable from the deck it came from in a list sorted by name.
|
|
121
|
+
*/
|
|
122
|
+
export declare function defaultMasterNameFor(sourceName?: string | null): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { CanvasCreateRequest, CanvasRetrieve } from '../../../types/openapi';
|
|
2
|
+
/**
|
|
3
|
+
* PIT-7488 — creating a master FROM AN ADMIN TEMPLATE.
|
|
4
|
+
*
|
|
5
|
+
* The fourth and last of the master copy paths, and the one that was missing: block 3 is
|
|
6
|
+
* deck -> master (`buildMasterContext`), block 4 is master -> deck (`buildContextFromMaster`),
|
|
7
|
+
* block 5 is master -> shared copy (next-core's `buildSharedCopyContext`), and this is
|
|
8
|
+
* template -> master.
|
|
9
|
+
*
|
|
10
|
+
* It exists because **Django does not clone a template server-side.** `POST /canvases` stores
|
|
11
|
+
* `template_id` as a plain FK and nothing else, so every create path clones client-side —
|
|
12
|
+
* `canvas.create.ts` for pitchdecks, `buildCreateFromMasterPayload` for decks from masters. The
|
|
13
|
+
* master tab was the one path that never did, so a master built from a template was created
|
|
14
|
+
* EMPTY: no `content` means no `SectionList`/`ContentList` nodes, and
|
|
15
|
+
* `useCanvasCompletionWizard.isCompletionWizardEnabled` is `wizardSteps.length > 0`, so the
|
|
16
|
+
* pitchdeck wizard could never run on it either. One missing clone, two reported symptoms.
|
|
17
|
+
*
|
|
18
|
+
* Not built on `slotty-ui-ng`'s shared `createCanvas` util on purpose: that util assigns
|
|
19
|
+
* `content`/`theme`/`context` AFTER spreading the caller's payload, so a sanitized master context
|
|
20
|
+
* cannot be passed in — and what it writes instead is wrong in both flag states (with
|
|
21
|
+
* `copy_context_to_canvas` on it copies the template context wholesale, identity and completed
|
|
22
|
+
* wizard state included; with it off it writes `{}`, losing `section_list_config`). It also
|
|
23
|
+
* prompts for an account and merges CRM data into the context on orgs with
|
|
24
|
+
* `require_account_on_canvas_creation`, which no master may carry.
|
|
25
|
+
*/
|
|
26
|
+
/** The narrow slice of a fetched template this needs — request it as `fields=content,theme,context`. */
|
|
27
|
+
export interface MasterTemplateSource extends Pick<CanvasRetrieve, 'content' | 'theme' | 'context'> {
|
|
28
|
+
}
|
|
29
|
+
/** The create-payload fields to spread over the master's own invariants. */
|
|
30
|
+
export interface MasterFromTemplatePayload extends Pick<CanvasCreateRequest, 'content' | 'theme' | 'context'> {
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Builds the `content`/`theme`/`context` a master should inherit from the template it was created
|
|
34
|
+
* from. Pure, non-mutating, and **deliberately ungated**.
|
|
35
|
+
*
|
|
36
|
+
* Not gated on `copy_context_to_canvas` for the same reason `buildMasterContext` is not: that flag
|
|
37
|
+
* exists because copying context wholesale was risky, and the risk *is* the identity keys stripped
|
|
38
|
+
* here. Gating on it would hand flag-off instances a master with no `section_list_config`, which is
|
|
39
|
+
* PIT-7473 reintroduced for every deck later built from that master.
|
|
40
|
+
*
|
|
41
|
+
* `metadata` is deliberately NOT copied. Nothing the wizard reads lives there, and a template's
|
|
42
|
+
* metadata carries its own bookkeeping — the async-download record (`job_id`, `pdf_url`, `pptx_url`,
|
|
43
|
+
* `conversion_date`) and `frozen_until`, which would make a brand-new master born read-only and
|
|
44
|
+
* offering a download of the TEMPLATE. Having none is the truthful state for a new master.
|
|
45
|
+
*
|
|
46
|
+
* Every field is omitted rather than emitted as `undefined`/`{}` when there is nothing to carry, so
|
|
47
|
+
* the no-template case ("New master pitchdeck") posts byte-identical to what it posts today instead
|
|
48
|
+
* of writing empty objects over the server's defaults.
|
|
49
|
+
*/
|
|
50
|
+
export declare function buildMasterFromTemplatePayload(template?: MasterTemplateSource | null): MasterFromTemplatePayload;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|