Skip to content

Commit 188322a

Browse files
Merge pull request #800 from back4app/bac-1380/add-migration-cards
migration cards
2 parents 5dc73f8 + ad1160d commit 188322a

3 files changed

Lines changed: 119 additions & 2 deletions

File tree

src/dashboard/Settings/Modals/editParseVersionModal.react.js

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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

src/dashboard/Settings/Modals/editParseVersionModal.scss

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,3 +227,59 @@
227227
font-size: 14px;
228228
}
229229

230+
/* Migration scheduling callouts */
231+
.migrationCard {
232+
display: flex;
233+
flex-direction: column;
234+
gap: 8px;
235+
margin-bottom: 12px;
236+
}
237+
238+
.migrationItem {
239+
background: $white;
240+
border: 1px solid rgba($dark, 0.15);
241+
border-radius: 8px;
242+
padding: 12px 16px;
243+
}
244+
245+
.migrationHead {
246+
display: flex;
247+
align-items: center;
248+
justify-content: space-between;
249+
gap: 12px;
250+
}
251+
252+
.migrationTitle {
253+
@include InterFont;
254+
font-size: 14px;
255+
font-weight: 500;
256+
color: $dark;
257+
line-height: 1.3;
258+
}
259+
260+
.migrationLink {
261+
display: inline-flex;
262+
align-items: center;
263+
justify-content: center;
264+
flex-shrink: 0;
265+
width: 24px;
266+
height: 24px;
267+
border-radius: 50%;
268+
269+
&:hover {
270+
cursor: pointer;
271+
}
272+
273+
&:focus {
274+
outline: none;
275+
}
276+
}
277+
278+
.migrationDescription {
279+
@include InterFont;
280+
font-size: 10px;
281+
color: rgba($dark, 0.65);
282+
margin: 6px 0 0;
283+
line-height: 1.45;
284+
}
285+

src/lib/ParseApp.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,11 @@ export default class ParseApp {
451451
return AJAX.get(path);
452452
}
453453

454+
parseServerMigrationLinks() {
455+
const path = `/parse-version/links/${this.slug}`;
456+
return AJAX.get(path);
457+
}
458+
454459
changeParseServerVersion(parseVersion) {
455460
const path = `/parse-version/${this.slug}/`;
456461
return AJAX.post(path, { parseVersion });

0 commit comments

Comments
 (0)