Skip to content

Commit 1238860

Browse files
authored
Merge branch 'master' into google-docs-document-outline
2 parents 72ee8de + 925b955 commit 1238860

84 files changed

Lines changed: 24135 additions & 46 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/google-docs/src/hooks/useWorkflowAgent.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ import { useState, useCallback } from 'react';
22
import { PageAppSDK } from '@contentful/app-sdk';
33
import { POLL_INTERVAL_MS, MAX_POLL_ATTEMPTS, WORKFLOW_AGENT_ID } from '../utils/constants/agent';
44
import {
5+
MappingReviewSuspendPayload,
56
ResumePayload,
6-
SuspendPayload,
7+
TabsImagesSuspendPayload,
78
PreviewPayload,
89
WorkflowRunResult,
910
RunStatus,
@@ -79,8 +80,11 @@ const getRunErrorMessage = (runData: AgentRunData): string => {
7980
return 'Workflow failed';
8081
};
8182

82-
const getSuspendPayload = (runData: AgentRunData): SuspendPayload | undefined =>
83-
runData.metadata?.suspendPayload as SuspendPayload | undefined;
83+
const getSuspendPayload = (
84+
runData: AgentRunData
85+
): TabsImagesSuspendPayload | MappingReviewSuspendPayload | undefined => {
86+
return runData.metadata?.suspendPayload;
87+
};
8488

8589
const getWorkflowRunResult = (
8690
runData: AgentRunData,

apps/google-docs/src/locations/Page/Page.tsx

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,18 @@ import {
88
} from './components/mainpage/ModalOrchestrator';
99
import { MainPageView } from './components/mainpage/MainPageView';
1010
import { PreviewPageView } from './components/mainpage/PreviewPageView';
11-
import { PreviewPayload } from '@types';
11+
import { MappingReviewSuspendPayload, PreviewPayload } from '@types';
12+
import { isMappingReviewSuspendPayload } from '../../utils/utils';
1213

1314
const Page = () => {
1415
const sdk = useSDK<PageAppSDK>();
1516
const modalOrchestratorRef = useRef<ModalOrchestratorHandle>(null);
1617
const [oauthToken, setOauthToken] = useState<string>('');
1718
const [isOAuthConnected, setIsOAuthConnected] = useState(false);
1819
const [isOAuthLoading, setIsOAuthLoading] = useState(true);
19-
const [previewPayload, setPreviewPayload] = useState<PreviewPayload | null>(null);
20+
const [previewPayload, setPreviewPayload] = useState<
21+
PreviewPayload | MappingReviewSuspendPayload | null
22+
>(null);
2023

2124
const handleOauthTokenChange = (token: string) => {
2225
setOauthToken(token);
@@ -38,18 +41,30 @@ const Page = () => {
3841
setPreviewPayload(payload);
3942
};
4043

41-
const handleUseFixturePreview = () => {
42-
setPreviewPayload({
43-
documentTitle: 'Fixture preview',
44-
data: {},
45-
});
44+
const handleMappingReviewReady = (payload: MappingReviewSuspendPayload) => {
45+
setPreviewPayload(payload);
4646
};
4747

4848
const handleReturnToMainPage = () => {
4949
modalOrchestratorRef.current?.resetFlowState();
5050
setPreviewPayload(null);
5151
};
5252

53+
const handleResumeMappingReview = async () => {
54+
if (!previewPayload || !isMappingReviewSuspendPayload(previewPayload)) {
55+
return;
56+
}
57+
58+
try {
59+
await modalOrchestratorRef.current?.resumeMappingReview(previewPayload);
60+
} catch (error) {
61+
console.error('Failed to resume mapping review:', error);
62+
sdk.notifier.error('Unable to resume preview. Please try again.');
63+
}
64+
};
65+
66+
console.log('previewPayload', previewPayload);
67+
5368
return (
5469
<>
5570
<Layout withBoxShadow={true} offsetTop={10}>
@@ -58,6 +73,7 @@ const Page = () => {
5873
payload={previewPayload}
5974
oauthToken={oauthToken}
6075
onLeavePreview={handleReturnToMainPage}
76+
onResumeMappingReview={handleResumeMappingReview}
6177
/>
6278
) : (
6379
<MainPageView
@@ -79,6 +95,7 @@ const Page = () => {
7995
sdk={sdk}
8096
oauthToken={oauthToken}
8197
onPreviewReady={handlePreviewReady}
98+
onMappingReviewReady={handleMappingReviewReady}
8299
onResetToMain={handleReturnToMainPage}
83100
/>
84101
</>

apps/google-docs/src/locations/Page/components/mainpage/ModalOrchestrator.tsx

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ import { CONTENT_TYPE_SUBMIT_LOADING_DELAY_MS } from '@constants/agent';
1111
import { SelectTabsModal } from '../modals/step_3/SelectTabsModal';
1212
import {
1313
DocumentTabProps,
14+
MappingReviewSuspendPayload,
1415
ResumePayload,
15-
SuspendPayload,
16+
TabsImagesSuspendPayload,
1617
PreviewPayload,
1718
RunStatus,
1819
WorkflowRunResult,
@@ -25,6 +26,7 @@ export interface ModalOrchestratorHandle {
2526
startFlow: () => void;
2627
/** Clears in-progress flow state without calling `onResetToMain` (parent clears preview separately). */
2728
resetFlowState: () => void;
29+
resumeMappingReview: (payload: MappingReviewSuspendPayload) => Promise<void>;
2830
}
2931

3032
enum FlowStep {
@@ -39,10 +41,11 @@ interface ModalOrchestratorProps {
3941
oauthToken: string;
4042
onPreviewReady: (payload: PreviewPayload) => void;
4143
onResetToMain: () => void;
44+
onMappingReviewReady: (payload: MappingReviewSuspendPayload) => void;
4245
}
4346

4447
export const ModalOrchestrator = forwardRef<ModalOrchestratorHandle, ModalOrchestratorProps>(
45-
({ sdk, oauthToken, onPreviewReady, onResetToMain }, ref) => {
48+
({ sdk, oauthToken, onPreviewReady, onMappingReviewReady, onResetToMain }, ref) => {
4649
const [isUploadModalOpen, setIsUploadModalOpen] = useState(false);
4750
const [isConfirmCancelModalOpen, setIsConfirmCancelModalOpen] = useState(false);
4851
const [isErrorPreviewModalOpen, setIsErrorPreviewModalOpen] = useState(false);
@@ -70,6 +73,19 @@ export const ModalOrchestrator = forwardRef<ModalOrchestratorHandle, ModalOrches
7073
setIsConfirmCancelModalOpen(false);
7174
setIsErrorPreviewModalOpen(false);
7275
},
76+
resumeMappingReview: async (payload: MappingReviewSuspendPayload) => {
77+
if (!activeRunId) {
78+
throw new Error('Workflow run id is missing for resume.');
79+
}
80+
81+
// TODO : modify the normalized document and entry block graph with the edited values
82+
const workflowRun = await resumeWorkflow(activeRunId, {
83+
editedNormalizedDocument: payload.normalizedDocument,
84+
entryBlockGraph: payload.entryBlockGraph,
85+
});
86+
87+
handleWorkflowResult(workflowRun);
88+
},
7389
}));
7490

7591
const resetDocumentScopeReview = () => {
@@ -117,7 +133,7 @@ export const ModalOrchestrator = forwardRef<ModalOrchestratorHandle, ModalOrches
117133
showDiscardConfirmation();
118134
};
119135

120-
const showDocumentScopeReview = (suspendPayload?: SuspendPayload) => {
136+
const showDocumentScopeReview = (suspendPayload?: TabsImagesSuspendPayload) => {
121137
setAvailableTabs(
122138
(suspendPayload?.tabs ?? []).map((tab) => ({
123139
tabId: tab.id ?? '',
@@ -146,6 +162,12 @@ export const ModalOrchestrator = forwardRef<ModalOrchestratorHandle, ModalOrches
146162
setActiveRunId(workflowRun.runId);
147163

148164
if (workflowRun.status === RunStatus.PENDING_REVIEW) {
165+
if (workflowRun.suspendPayload.suspendStepId === 'mapping-review') {
166+
setFlowStep(null);
167+
onMappingReviewReady(workflowRun.suspendPayload);
168+
return;
169+
}
170+
149171
showDocumentScopeReview(workflowRun.suspendPayload);
150172
return;
151173
}

apps/google-docs/src/locations/Page/components/mainpage/PreviewPageView.tsx

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,32 @@
11
import { useState } from 'react';
22
import { Button, Flex, Heading, Layout, Note, Paragraph } from '@contentful/f36-components';
33
import Splitter from './Splitter';
4-
import { PreviewPayload } from '@types';
4+
import { MappingReviewSuspendPayload, PreviewPayload } from '@types';
55
import { ConfirmCancelModal } from '../modals/ConfirmCancelModal';
66
import { loadGoogleDocsReviewFixture } from '../../../../fixtures/googleDocsReview';
77
import { GoogleDocsMappingReviewScreen } from '../review-prototype/GoogleDocsMappingReviewScreen';
88
import Splitter from './Splitter';
99
import OverviewSection from '../overview/OverviewSection';
1010
import { useSDK } from '@contentful/react-apps-toolkit';
1111
import { PageAppSDK } from '@contentful/app-sdk';
12+
import { isMappingReviewSuspendPayload } from '../../../../utils/utils';
1213

1314
interface PreviewPageViewProps {
14-
payload: PreviewPayload;
15+
payload: PreviewPayload | MappingReviewSuspendPayload;
1516
oauthToken: string;
1617
onLeavePreview: () => void;
18+
onResumeMappingReview?: () => Promise<void>;
1719
}
1820

19-
export const PreviewPageView = ({ payload, oauthToken, onLeavePreview }: PreviewPageViewProps) => {
21+
export const PreviewPageView = ({
22+
payload,
23+
oauthToken,
24+
onLeavePreview,
25+
onResumeMappingReview,
26+
}: PreviewPageViewProps) => {
2027
const sdk = useSDK<PageAppSDK>();
2128
const [isConfirmCancelModalOpen, setIsConfirmCancelModalOpen] = useState(false);
22-
const fixture = loadGoogleDocsReviewFixture();
23-
24-
const rawTitle = payload.normalizedDocument?.title as string | undefined;
25-
const title = rawTitle?.trim() ? rawTitle : 'Selected document';
29+
const mappingReviewPayload = isMappingReviewSuspendPayload(payload) ? payload : null;
2630
const rawTitle = payload.normalizedDocument?.title;
2731
const docTitle = typeof rawTitle === 'string' ? rawTitle : undefined;
2832
const title = docTitle && docTitle.trim().length > 0 ? docTitle : 'Selected document';
@@ -51,6 +55,7 @@ export const PreviewPageView = ({ payload, oauthToken, onLeavePreview }: Preview
5155
payload={payload}
5256
oauthToken={oauthToken}
5357
onReturnToMainPage={onLeavePreview}
58+
onCreateSelected={mappingReviewPayload ? onResumeMappingReview : undefined}
5459
/>
5560
<Heading as="h2" marginBottom="none">
5661
Document outline

apps/google-docs/src/locations/Page/components/overview/OverviewSection.tsx

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useEffect, useMemo, useState } from 'react';
22
import { cx } from '@emotion/css';
33
import { Box, Button, Flex, Heading, Note, Paragraph } from '@contentful/f36-components';
4-
import type { PreviewPayload } from '@types';
4+
import type { MappingReviewSuspendPayload, PreviewPayload } from '@types';
55
import {
66
buildCheckboxEntryList,
77
collectCheckboxEntryListRowIds,
@@ -10,23 +10,25 @@ import {
1010
import { fetchContentTypesInfoByIds } from '../../../../services/contentTypeService';
1111
import { CheckboxEntryList } from './CheckboxEntryList';
1212
import { overviewSectionBox, overviewSectionBoxScrollable } from './OverviewSection.styles';
13-
import { createEntriesFromPreviewPayload } from '../../../../services/entryService';
1413
import { PageAppSDK } from '@contentful/app-sdk';
1514
import type { EntryProps } from 'contentful-management';
1615
import { SummaryModal } from '../modals/SummaryModal';
16+
import { isPreviewPayload } from '../../../../utils/utils';
1717

1818
interface OverviewSectionProps {
1919
sdk: PageAppSDK;
20-
payload: PreviewPayload;
20+
payload: PreviewPayload | MappingReviewSuspendPayload;
2121
oauthToken: string;
2222
onReturnToMainPage: () => void;
23+
onCreateSelected?: () => Promise<void>;
2324
}
2425

2526
const OverviewSection = ({
2627
sdk,
2728
payload,
2829
oauthToken,
2930
onReturnToMainPage,
31+
onCreateSelected,
3032
}: OverviewSectionProps) => {
3133
const [contentTypeDisplayInfoMap, setContentTypeDisplayInfoMap] = useState<
3234
ContentTypeDisplayInfoMap | undefined
@@ -35,9 +37,38 @@ const OverviewSection = ({
3537
const [isCreating, setIsCreating] = useState(false);
3638
const [summaryEntries, setSummaryEntries] = useState<EntryProps[] | null>(null);
3739

40+
const overviewPayload = useMemo<PreviewPayload>(() => {
41+
if (isPreviewPayload(payload)) {
42+
return payload;
43+
}
44+
45+
return {
46+
// TODO: remove this temporary mock once the backend provides preview entries
47+
entries: [
48+
{
49+
fields: {
50+
url: {
51+
'en-US': '/blog/an-url',
52+
},
53+
internalLabel: {
54+
'en-US': '/blog/another-url',
55+
},
56+
},
57+
tempId: 'url_1',
58+
contentTypeId: 'url',
59+
},
60+
],
61+
assets: [],
62+
referenceGraph: payload.referenceGraph,
63+
normalizedDocument: payload.normalizedDocument,
64+
};
65+
}, [payload]);
66+
3867
useEffect(() => {
3968
const fetchContentTypesInfo = async () => {
40-
const contentTypeIds = [...new Set(payload.entries.map((entry) => entry.contentTypeId))]
69+
const contentTypeIds = [
70+
...new Set(overviewPayload.entries.map((entry) => entry.contentTypeId)),
71+
]
4172
.filter((id): id is string => Boolean(id))
4273
.sort();
4374
if (contentTypeIds.length === 0) {
@@ -54,11 +85,11 @@ const OverviewSection = ({
5485
};
5586

5687
fetchContentTypesInfo();
57-
}, [sdk, payload.entries]);
88+
}, [sdk, overviewPayload.entries]);
5889

5990
const checkboxEntryRows = useMemo(
60-
() => buildCheckboxEntryList(payload, contentTypeDisplayInfoMap, sdk.locales.default),
61-
[payload, contentTypeDisplayInfoMap, sdk.locales.default]
91+
() => buildCheckboxEntryList(overviewPayload, contentTypeDisplayInfoMap, sdk.locales.default),
92+
[overviewPayload, contentTypeDisplayInfoMap, sdk.locales.default]
6293
);
6394

6495
useEffect(() => {
@@ -78,24 +109,14 @@ const OverviewSection = ({
78109
};
79110

80111
const handleCreateSelected = async () => {
81-
if (selectedEntryTempIds.size === 0) {
112+
if (selectedEntryTempIds.size === 0 || !onCreateSelected) {
82113
return;
83114
}
115+
84116
setIsCreating(true);
117+
85118
try {
86-
const result = await createEntriesFromPreviewPayload(
87-
sdk,
88-
payload,
89-
selectedEntryTempIds,
90-
oauthToken
91-
);
92-
if (result.errors.length > 0) {
93-
sdk.notifier.error('Failed to create entries');
94-
} else {
95-
setSummaryEntries(result.createdEntries);
96-
}
97-
} catch {
98-
sdk.notifier.error('Failed to create entries');
119+
await onCreateSelected();
99120
} finally {
100121
setIsCreating(false);
101122
}
@@ -128,7 +149,7 @@ const OverviewSection = ({
128149
variant="primary"
129150
onClick={handleCreateSelected}
130151
isLoading={isCreating}
131-
isDisabled={selectedEntryTempIds.size === 0}>
152+
isDisabled={selectedEntryTempIds.size === 0 || !onCreateSelected}>
132153
Create selected entries
133154
</Button>
134155
</Flex>

apps/google-docs/src/services/agents-api.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { PageAppSDK } from '@contentful/app-sdk';
22
import { LOCAL_AGENTS_API_BASE_URL, WORKFLOW_AGENT_ID } from '../utils/constants/agent';
3-
import { AgentRunMessage, ResumePayload, RunStatus } from '@types';
3+
import {
4+
AgentRunMessage,
5+
MappingReviewSuspendPayload,
6+
ResumePayload,
7+
RunStatus,
8+
TabsImagesSuspendPayload,
9+
} from '@types';
410

511
const AGENTS_API_HEADERS = {
612
'x-contentful-enable-alpha-feature': 'agents-api',
@@ -32,7 +38,7 @@ export interface AgentRunData {
3238
status?: RunStatus;
3339
workflowId?: string;
3440
workflowRunId?: string;
35-
suspendPayload?: Record<string, unknown>;
41+
suspendPayload?: TabsImagesSuspendPayload | MappingReviewSuspendPayload;
3642
googleDocPayload?: Record<string, unknown>;
3743
};
3844
payload?: string;

0 commit comments

Comments
 (0)