1- import { useState , useEffect } from 'react' ;
2- import { Box , Button , Form , FormControl , TextInput } from '@contentful/f36-components' ;
1+ import { useState } from 'react' ;
2+ import {
3+ Box ,
4+ Button ,
5+ Form ,
6+ FormControl ,
7+ Modal ,
8+ Stack ,
9+ Text ,
10+ Card ,
11+ } from '@contentful/f36-components' ;
312import { PageAppSDK } from '@contentful/app-sdk' ;
13+ import { TEST_DOCUMENTS } from '../../utils/test_docs_json' ;
414
515interface GoogleDocUploaderProps {
616 sdk : PageAppSDK ;
@@ -9,107 +19,33 @@ interface GoogleDocUploaderProps {
919 isDisabled ?: boolean ;
1020}
1121
12- function isValidGoogleDocUrl ( url : string ) : boolean {
13- return / ^ h t t p s : \/ \/ d o c s \. g o o g l e \. c o m \/ d o c u m e n t \/ d \/ [ A - Z a - z 0 - 9 _ - ] + \/ e d i t (?: \? [ ^ # ] * ) ? $ / . test ( url ) ;
14- }
15-
16- const extractGoogleDocId = ( url : string ) : string | null => {
17- const patterns = [
18- / \/ d o c u m e n t \/ d \/ ( [ a - z A - Z 0 - 9 _ - ] + ) / , // https://docs.google.com/document/d/{id}/...
19- / \/ u \/ \d \/ d \/ ( [ a - z A - Z 0 - 9 _ - ] + ) / , // https://docs.google.com/document/u/0/d/{id}/...
20- ] ;
21- for ( const rx of patterns ) {
22- const m = url . match ( rx ) ;
23- if ( m && m [ 1 ] ) return m [ 1 ] ;
24- }
25- return null ;
26- } ;
27-
28- const extractPublishedDocId = ( url : string ) : string | null => {
29- // Published Google Docs use /document/d/e/{publishedId}/...
30- const m = url . match ( / \/ d o c u m e n t \/ d \/ e \/ ( [ a - z A - Z 0 - 9 _ - ] + ) / ) ;
31- return m && m [ 1 ] ? m [ 1 ] : null ;
32- } ;
33-
34- const fetchGoogleDoc = async ( url : string ) => {
35- // If this is a "published to web" URL, fetch from the public export endpoint (no OAuth needed)
36- const publishedId = extractPublishedDocId ( url ) ;
37- if ( publishedId ) {
38- const publishedExportUrl = `https://docs.google.com/document/d/e/${ publishedId } /export?format=html` ;
39- const publishedResp = await fetch ( publishedExportUrl , { method : 'GET' } ) ;
40- if ( ! publishedResp . ok ) {
41- throw new Error ( `Failed to fetch published document (status ${ publishedResp . status } ).` ) ;
42- }
43- const publishedHtml = await publishedResp . text ( ) ;
44- return { title : 'Google Document' , html : publishedHtml } ;
45- }
46-
47- const docId = extractGoogleDocId ( url ) ;
48- if ( ! docId ) {
49- throw new Error ( 'Unable to extract Google Doc ID from URL.' ) ;
50- }
51- // Try docs.google.com export first (no OAuth); if that fails, fall back to Drive API
52- try {
53- const directExportUrl = `https://docs.google.com/document/d/${ encodeURIComponent (
54- docId
55- ) } /export?format=html`;
56- const directResp = await fetch ( directExportUrl , { method : 'GET' } ) ;
57- if ( directResp . ok ) {
58- const directHtml = await directResp . text ( ) ;
59- return { title : 'Google Document' , html : directHtml } ;
60- }
61- } catch {
62- // ignore and continue
63- }
64- return { title : 'Google Document' , html : null } ;
65- } ;
66-
6722export const GoogleDocUploader = ( {
6823 sdk,
6924 onSuccess,
7025 onError,
7126 isDisabled,
7227} : GoogleDocUploaderProps ) => {
73- const [ googleDocUrl , setGoogleDocUrl ] = useState < string > ( '' ) ;
74- const [ googleDocUrlValid , setGoogleDocUrlValid ] = useState < boolean > ( true ) ;
28+ const [ selectedDocument , setSelectedDocument ] = useState < string > ( '' ) ;
7529 const [ isUploading , setIsUploading ] = useState < boolean > ( false ) ;
30+ const [ isModalOpen , setIsModalOpen ] = useState < boolean > ( false ) ;
7631
77- const validateGoogleDocUrl = ( value : string ) => {
78- const trimmed = value . trim ( ) ;
79- if ( trimmed . length === 0 ) {
80- setGoogleDocUrlValid ( true ) ;
81- return true ;
82- }
83- // Minimal validation: must be a docs.google.com document URL
84- const isValid =
85- / ^ h t t p s ? : \/ \/ d o c s \. g o o g l e \. c o m \/ d o c u m e n t \/ d \/ [ a - z A - Z 0 - 9 - _ ] + / . test ( trimmed ) ||
86- / ^ h t t p s ? : \/ \/ d o c s \. g o o g l e \. c o m \/ d o c u m e n t \/ u \/ \d \/ d \/ [ a - z A - Z 0 - 9 - _ ] + / . test ( trimmed ) ;
87- setGoogleDocUrlValid ( isValid ) ;
88- return isValid ;
89- } ;
90-
91- useEffect ( ( ) => {
92- if ( googleDocUrl ) {
93- validateGoogleDocUrl ( googleDocUrl ) ;
94- }
95- } , [ googleDocUrl ] ) ;
32+ const handleSelectDocument = async ( docId : string , title : string , documentData : any ) => {
33+ setSelectedDocument ( title ) ;
34+ setIsModalOpen ( false ) ;
9635
97- const onSubmitGoogleDoc = async ( ) => {
98- const isValid = validateGoogleDocUrl ( googleDocUrl ) ;
99- if ( ! isValid ) {
100- sdk . notifier . error ( 'Please enter a valid public Google Doc link.' ) ;
101- return ;
102- }
10336 try {
10437 setIsUploading ( true ) ;
105- const result = await fetchGoogleDoc ( googleDocUrl ) ;
106- onSuccess ( result . title , result . html ) ;
107- sdk . notifier . success ( 'Google Doc uploaded successfully' ) ;
38+
39+ // Log the document data to console
40+ console . log ( 'Selected document:' , title ) ;
41+ console . log ( 'Document data:' , documentData ) ;
42+
43+ sdk . notifier . success ( `Document "${ title } " loaded successfully` ) ;
44+
45+ // Proceed to content type selector with the document data
46+ onSuccess ( title , JSON . stringify ( documentData ) ) ;
10847 } catch ( e : unknown ) {
109- const message =
110- e instanceof Error
111- ? e . message
112- : 'Failed to fetch Google Doc. Ensure it is publicly accessible.' ;
48+ const message = e instanceof Error ? e . message : 'Failed to load document.' ;
11349 onError ( message ) ;
11450 sdk . notifier . error ( message ) ;
11551 } finally {
@@ -120,45 +56,54 @@ export const GoogleDocUploader = ({
12056 return (
12157 < Form >
12258 < FormControl >
123- < FormControl . Label > Public Google Doc link</ FormControl . Label >
124- < TextInput
125- id = "googleDocUrl"
126- name = "googleDocUrl"
127- value = { googleDocUrl }
128- isInvalid = { ! googleDocUrlValid }
129- placeholder = "https://docs.google.com/document/d/..."
130- onChange = { ( e ) => {
131- const url = e . target . value ;
132- setGoogleDocUrl ( url ) ;
133- setGoogleDocUrlValid ( isValidGoogleDocUrl ( url ) ) ;
134- } }
135- />
136- < FormControl . HelpText >
137- Must be a publicly accessible Google Docs URL (View access)
138- </ FormControl . HelpText >
139- { googleDocUrlValid && googleDocUrl && (
59+ < Button
60+ variant = "secondary"
61+ onClick = { ( ) => setIsModalOpen ( true ) }
62+ isDisabled = { isDisabled || isUploading } >
63+ { selectedDocument ? 'Change Document' : 'Select Document' }
64+ </ Button >
65+
66+ { selectedDocument && (
14067 < Box marginTop = "spacingS" >
141- < a
142- href = { encodeURI ( googleDocUrl . replace ( '/edit' , '/preview' ) ) }
143- target = "_blank"
144- rel = "noopener noreferrer" >
145- Open original (best formatting)
146- </ a >
68+ < Text fontWeight = "fontWeightDemiBold" > Selected: { selectedDocument } </ Text >
14769 </ Box >
14870 ) }
149- { ! googleDocUrlValid && (
150- < FormControl . ValidationMessage >
151- Enter a valid Google Doc URL.
152- </ FormControl . ValidationMessage >
71+
72+ { isUploading && (
73+ < Box marginTop = "spacingS" >
74+ < Text fontColor = "gray500" fontSize = "fontSizeS" >
75+ Processing document...
76+ </ Text >
77+ </ Box >
15378 ) }
154- < Box marginTop = "spacingS" >
155- < Button
156- isDisabled = { isDisabled || isUploading || ! googleDocUrl || ! googleDocUrlValid }
157- onClick = { onSubmitGoogleDoc } >
158- { isUploading ? 'Uploading...' : 'Upload Google Doc' }
159- </ Button >
160- </ Box >
79+
80+ < FormControl . HelpText >
81+ Choose a test document to process with the agent
82+ </ FormControl . HelpText >
16183 </ FormControl >
84+
85+ < Modal onClose = { ( ) => setIsModalOpen ( false ) } isShown = { isModalOpen } size = "large" >
86+ { ( ) => (
87+ < >
88+ < Modal . Header title = "Select a Test Document" onClose = { ( ) => setIsModalOpen ( false ) } />
89+ < Modal . Content >
90+ < Stack flexDirection = "column" spacing = "spacingS" >
91+ { TEST_DOCUMENTS . map ( ( doc , index ) => (
92+ < Card
93+ key = { index }
94+ as = "button"
95+ onClick = { ( ) => handleSelectDocument ( doc . id , doc . title , doc . data ) }
96+ style = { { cursor : 'pointer' , textAlign : 'left' , padding : '12px' } } >
97+ < Stack flexDirection = "column" spacing = "spacingXs" >
98+ < Text fontWeight = "fontWeightDemiBold" > { doc . title } </ Text >
99+ </ Stack >
100+ </ Card >
101+ ) ) }
102+ </ Stack >
103+ </ Modal . Content >
104+ </ >
105+ ) }
106+ </ Modal >
162107 </ Form >
163108 ) ;
164109} ;
0 commit comments