1- import { ConfigAppSDK } from '@contentful/app-sdk' ;
2- import { Flex , Form , Heading , Paragraph } from '@contentful/f36-components' ;
3- import { /* useCMA, */ useSDK } from '@contentful/react-apps-toolkit' ;
4- import { css } from 'emotion' ;
1+ import { ConfigAppSDK , AppState } from '@contentful/app-sdk' ;
2+ import { Flex , Heading , Paragraph , FormControl } from '@contentful/f36-components' ;
3+ import { useSDK } from '@contentful/react-apps-toolkit' ;
54import { useCallback , useEffect , useState } from 'react' ;
5+ import { ContentTypeProps } from 'contentful-management' ;
6+ import ContentTypeMultiSelect from '../components/ContentTypeMultiSelect' ;
7+ import { styles } from './ConfigScreen.styles' ;
68
79export type AppInstallationParameters = Record < string , unknown > ;
810
911const ConfigScreen = ( ) => {
1012 const [ parameters , setParameters ] = useState < AppInstallationParameters > ( { } ) ;
13+ const [ allContentTypes , setAllContentTypes ] = useState < ContentTypeProps [ ] > ( [ ] ) ;
14+ const [ selectedContentTypes , setSelectedContentTypes ] = useState < ContentTypeProps [ ] > ( [ ] ) ;
15+ const [ isLoading , setIsLoading ] = useState ( true ) ;
1116 const sdk = useSDK < ConfigAppSDK > ( ) ;
1217
18+ const fetchAllContentTypes = async ( ) : Promise < ContentTypeProps [ ] > => {
19+ const contentTypes : ContentTypeProps [ ] = [ ] ;
20+ let skip = 0 ;
21+ const limit = 1000 ;
22+ let fetched : number ;
23+
24+ do {
25+ const response = await sdk . cma . contentType . getMany ( {
26+ spaceId : sdk . ids . space ,
27+ environmentId : sdk . ids . environment ,
28+ query : { skip, limit } ,
29+ } ) ;
30+ const items = response . items as ContentTypeProps [ ] ;
31+ contentTypes . push ( ...items ) ;
32+ fetched = items . length ;
33+ skip += limit ;
34+ } while ( fetched === limit ) ;
35+
36+ return contentTypes . sort ( ( a , b ) => a . name . localeCompare ( b . name ) ) ;
37+ } ;
38+
39+ const loadContentTypesAndRestoreState = async ( ) => {
40+ try {
41+ setIsLoading ( true ) ;
42+ const contentTypes = await fetchAllContentTypes ( ) ;
43+ setAllContentTypes ( contentTypes ) ;
44+
45+ // Restore selected content types from saved state
46+ const currentState : AppState | null = await sdk . app . getCurrentState ( ) ;
47+ if ( currentState ?. EditorInterface ) {
48+ const selectedIds = Object . keys ( currentState . EditorInterface ) ;
49+ const restored = contentTypes . filter ( ( ct ) => selectedIds . includes ( ct . sys . id ) ) ;
50+ setSelectedContentTypes ( restored ) ;
51+ }
52+ } catch ( error ) {
53+ console . error ( 'Error loading content types:' , error ) ;
54+ } finally {
55+ setIsLoading ( false ) ;
56+ }
57+ } ;
58+
1359 const onConfigure = useCallback ( async ( ) => {
1460 const currentState = await sdk . app . getCurrentState ( ) ;
61+ const currentEditorInterface = currentState ?. EditorInterface || { } ;
62+
63+ // Build new EditorInterface with selected content types assigned to sidebar
64+ const newEditorInterface : AppState [ 'EditorInterface' ] = { } ;
65+
66+ // Remove content types that are no longer selected
67+ Object . keys ( currentEditorInterface ) . forEach ( ( contentTypeId ) => {
68+ if ( selectedContentTypes . some ( ( ct ) => ct . sys . id === contentTypeId ) ) {
69+ newEditorInterface [ contentTypeId ] = currentEditorInterface [ contentTypeId ] ;
70+ }
71+ } ) ;
72+
73+ // Add newly selected content types to sidebar
74+ selectedContentTypes . forEach ( ( contentType ) => {
75+ if ( ! newEditorInterface [ contentType . sys . id ] ) {
76+ newEditorInterface [ contentType . sys . id ] = {
77+ sidebar : { position : 1 } ,
78+ } ;
79+ }
80+ } ) ;
1581
1682 return {
1783 parameters,
18- targetState : currentState ,
84+ targetState : {
85+ EditorInterface : newEditorInterface ,
86+ } ,
1987 } ;
20- } , [ parameters , sdk ] ) ;
88+ } , [ parameters , selectedContentTypes , sdk ] ) ;
2189
2290 useEffect ( ( ) => {
2391 sdk . app . onConfigure ( ( ) => onConfigure ( ) ) ;
@@ -31,16 +99,48 @@ const ConfigScreen = () => {
3199 setParameters ( currentParameters ) ;
32100 }
33101
102+ await loadContentTypesAndRestoreState ( ) ;
34103 sdk . app . setReady ( ) ;
35104 } ) ( ) ;
36105 } , [ sdk ] ) ;
37106
107+ if ( isLoading ) {
108+ return (
109+ < Flex justifyContent = "center" alignItems = "center" >
110+ < Paragraph > Loading content types...</ Paragraph >
111+ </ Flex >
112+ ) ;
113+ }
114+
38115 return (
39- < Flex flexDirection = "column" className = { css ( { margin : '80px' , maxWidth : '800px' } ) } >
40- < Form >
41- < Heading > App Config</ Heading >
42- < Paragraph > Welcome to your contentful app. This is your config page.</ Paragraph >
43- </ Form >
116+ < Flex justifyContent = "center" marginTop = "spacingL" marginLeft = "spacingL" marginRight = "spacingL" >
117+ < Flex className = { styles . container } flexDirection = "column" alignItems = "flex-start" >
118+ < Flex flexDirection = "column" alignItems = "flex-start" >
119+ < Heading marginBottom = "spacingS" > Set up Field Populator</ Heading >
120+ < Paragraph marginBottom = "spacing2Xl" >
121+ Save time localizing content by instantly copying field values across locales with the
122+ Field Populator app.
123+ </ Paragraph >
124+ </ Flex >
125+ < Flex flexDirection = "column" alignItems = "flex-start" >
126+ < Heading as = "h3" marginBottom = "spacingXs" >
127+ Assign content types
128+ </ Heading >
129+ < Paragraph marginBottom = "spacingL" >
130+ { `Select the content type(s) you want to use with Field Populator. You can change this
131+ anytime by navigating to the 'Sidebar' tab in your content model.` }
132+ </ Paragraph >
133+ < FormControl id = "contentTypes" style = { { width : '100%' } } >
134+ < FormControl . Label > Content types</ FormControl . Label >
135+ < ContentTypeMultiSelect
136+ availableContentTypes = { allContentTypes }
137+ selectedContentTypes = { selectedContentTypes }
138+ onSelectionChange = { setSelectedContentTypes }
139+ isDisabled = { allContentTypes . length === 0 }
140+ />
141+ </ FormControl >
142+ </ Flex >
143+ </ Flex >
44144 </ Flex >
45145 ) ;
46146} ;
0 commit comments