@@ -16,6 +16,9 @@ export const EditParseVersionModal = ({ context, setParentState, currentParseVer
1616 const [ dropdownOpen , setDropdownOpen ] = useState ( false ) ;
1717 const [ note , setNote ] = useState ( '' ) ;
1818 const [ noteColor , setNoteColor ] = useState ( 'red' ) ;
19+ const [ migrationLinks , setMigrationLinks ] = useState ( [ ] ) ;
20+
21+ const isGDPR = ! ! ( context && context . custom && context . custom . isGDPR ) ;
1922
2023 const parseDependencies = ( deps ) => {
2124 if ( ! deps ) {
@@ -66,7 +69,7 @@ export const EditParseVersionModal = ({ context, setParentState, currentParseVer
6669
6770 useEffect ( ( ) => {
6871 setProcessing ( true ) ;
69- context . supportedParseServerVersionsForApp ( )
72+ const versionsPromise = context . supportedParseServerVersionsForApp ( )
7073 . then ( ( data ) => {
7174 const versions = Array . isArray ( data ) ? data : ( data . results || [ ] ) ;
7275 setParseVersions ( versions ) ;
@@ -77,13 +80,28 @@ export const EditParseVersionModal = ({ context, setParentState, currentParseVer
7780 setNote ( e . error || 'Failed to load versions' ) ;
7881 console . log ( 'e' , e ) ;
7982 setNoteColor ( 'red' ) ;
83+ } ) ;
84+
85+ // Migration links are best-effort: if they fail we still show the version picker.
86+ const linksPromise = context . parseServerMigrationLinks ( )
87+ . then ( ( data ) => {
88+ const links = Array . isArray ( data ) ? data : ( data ?. results || [ ] ) ;
89+ setMigrationLinks ( links ) ;
8090 } )
81- . finally ( ( ) => setProcessing ( false ) ) ;
91+ . catch ( ( ) => {
92+ setMigrationLinks ( [ ] ) ;
93+ } ) ;
94+
95+ Promise . all ( [ versionsPromise , linksPromise ] ) . finally ( ( ) => setProcessing ( false ) ) ;
8296 } , [ ] ) ;
8397
8498 const npmModules = parseDependencies ( selectedVersion ?. dependencies ?? selectedVersion ?. npmModules ) ;
8599 const hasSelectedVersion = ! ! selectedVersion ?. version ;
86100
101+ const visibleMigrations = isGDPR
102+ ? [ ]
103+ : ( migrationLinks || [ ] ) . filter ( ( m ) => m && m . link && m . version !== selectedVersion ?. version ) ;
104+
87105 const close = ( ) => setParentState ( { showEditParseVersionModal : false } ) ;
88106
89107 const save = async ( ) => {
@@ -126,6 +144,44 @@ export const EditParseVersionModal = ({ context, setParentState, currentParseVer
126144 </ div >
127145
128146 < div className = { styles . modalBody } >
147+ { visibleMigrations . length > 0 && (
148+ < div className = { styles . migrationCard } >
149+ { visibleMigrations . map ( ( m ) => (
150+ < div key = { m . id || m . _id || m . version } className = { styles . migrationItem } >
151+ < div className = { styles . migrationHead } >
152+ < span className = { styles . migrationTitle } >
153+ Upgrade to the latest Parse Server
154+ </ span >
155+ < a
156+ className = { styles . migrationLink }
157+ href = { `${ m . link } ?appId=${ context . applicationId } ` }
158+ target = '_blank'
159+ rel = 'noopener noreferrer'
160+ aria-label = 'Schedule migration'
161+ title = 'Schedule migration'
162+ >
163+ < svg
164+ width = '18'
165+ height = '18'
166+ viewBox = '0 0 24 24'
167+ fill = '#27AE60'
168+ aria-hidden = 'true'
169+ >
170+ < path
171+ fillRule = 'evenodd'
172+ clipRule = 'evenodd'
173+ d = 'M8 1.5a1 1 0 0 1 1 1V4h6V2.5a1 1 0 1 1 2 0V4h1.5A2.5 2.5 0 0 1 21 6.5V9H3V6.5A2.5 2.5 0 0 1 5.5 4H7V2.5a1 1 0 0 1 1-1ZM3 10.5h18v8A2.5 2.5 0 0 1 18.5 21h-13A2.5 2.5 0 0 1 3 18.5v-8ZM7.25 12.5h1.5a.5.5 0 0 1 .5.5v1.5a.5.5 0 0 1-.5.5h-1.5a.5.5 0 0 1-.5-.5v-1.5a.5.5 0 0 1 .5-.5Zm4 0h1.5a.5.5 0 0 1 .5.5v1.5a.5.5 0 0 1-.5.5h-1.5a.5.5 0 0 1-.5-.5v-1.5a.5.5 0 0 1 .5-.5Zm4 0h1.5a.5.5 0 0 1 .5.5v1.5a.5.5 0 0 1-.5.5h-1.5a.5.5 0 0 1-.5-.5v-1.5a.5.5 0 0 1 .5-.5Zm-8 4h1.5a.5.5 0 0 1 .5.5v1.5a.5.5 0 0 1-.5.5h-1.5a.5.5 0 0 1-.5-.5V17a.5.5 0 0 1 .5-.5Zm4 0h1.5a.5.5 0 0 1 .5.5v1.5a.5.5 0 0 1-.5.5h-1.5a.5.5 0 0 1-.5-.5V17a.5.5 0 0 1 .5-.5Zm4 0h1.5a.5.5 0 0 1 .5.5v1.5a.5.5 0 0 1-.5.5h-1.5a.5.5 0 0 1-.5-.5V17a.5.5 0 0 1 .5-.5Z'
174+ />
175+ </ svg >
176+ </ a >
177+ </ div >
178+ { m . description && (
179+ < p className = { styles . migrationDescription } > { m . description } </ p >
180+ ) }
181+ </ div >
182+ ) ) }
183+ </ div >
184+ ) }
129185 < div className = { styles . content } >
130186
131187
0 commit comments