Skip to content

Commit 307c455

Browse files
committed
feat(graph): show next steps for successful migrations
1 parent 13f57d6 commit 307c455

File tree

3 files changed

+60
-7
lines changed

3 files changed

+60
-7
lines changed

graph/migrate/src/lib/components/migration-card.tsx

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,13 @@ import {
1212
PlayIcon,
1313
} from '@heroicons/react/24/outline';
1414
import { Pill } from '@nx/graph-internal/ui-project-details';
15-
import { useState, forwardRef, useImperativeHandle, useEffect } from 'react';
15+
import {
16+
useState,
17+
forwardRef,
18+
useImperativeHandle,
19+
useEffect,
20+
type ReactNode,
21+
} from 'react';
1622
import { AnimatePresence, motion } from 'framer-motion';
1723

1824
export interface MigrationCardHandle {
@@ -21,6 +27,30 @@ export interface MigrationCardHandle {
2127
toggle: () => void;
2228
}
2329

30+
function convertUrlsToLinks(text: string): ReactNode[] {
31+
const urlRegex = /(https?:\/\/[^\s]+)/g;
32+
const parts = text.split(urlRegex);
33+
const urls = text.match(urlRegex) || [];
34+
const result: ReactNode[] = [];
35+
for (let i = 0; i < parts.length; i++) {
36+
if (urls[i - 1]) {
37+
result.push(
38+
<a
39+
key={i}
40+
href={urls[i - 1]}
41+
target="_blank"
42+
className="text-blue-500 hover:underline"
43+
>
44+
{urls[i - 1]}
45+
</a>
46+
);
47+
} else if (parts[i]) {
48+
result.push(parts[i]);
49+
}
50+
}
51+
return result;
52+
}
53+
2454
export const MigrationCard = forwardRef<
2555
MigrationCardHandle,
2656
{
@@ -200,6 +230,23 @@ export const MigrationCard = forwardRef<
200230
)}
201231
</div>
202232
</div>
233+
{succeeded && migrationResult?.nextSteps?.length ? (
234+
<div className="pt-2">
235+
<div className="my-2 border-t border-slate-200 dark:border-slate-700/60" />
236+
<span className="pb-2 text-sm font-bold">
237+
More Information & Next Steps
238+
</span>
239+
<ul className="list-inside list-disc pl-2">
240+
{migrationResult?.nextSteps.map((step, idx) => (
241+
<li key={idx} className="text-sm">
242+
{convertUrlsToLinks(step)}
243+
</li>
244+
))}
245+
</ul>
246+
<p></p>
247+
</div>
248+
) : null}
249+
203250
<div className="mt-4 flex justify-end gap-2">
204251
<button
205252
onClick={() => onViewImplementation()}
@@ -266,7 +313,7 @@ export const MigrationCard = forwardRef<
266313
onFileClick(file);
267314
}}
268315
>
269-
{file.path}
316+
<code>{file.path}</code>
270317
</li>
271318
);
272319
})}

graph/migrate/src/lib/migrate.stories.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,15 +242,15 @@ export const PendingApproval: Story = {
242242
{
243243
id: 'migration-1',
244244
name: 'migration-1',
245-
description: 'Migrate 1',
245+
description: 'This is a test migration',
246246
version: '1.0.0',
247247
package: 'nx',
248248
implementation: './src/migrations/migration-1.ts',
249249
},
250250
{
251251
id: 'migration-2',
252252
name: 'migration-2',
253-
description: 'Migrate 2',
253+
description: 'This is a test migration',
254254
version: '1.0.1',
255255
package: '@nx/react',
256256
implementation: './src/migrations/migration-2.ts',
@@ -271,12 +271,14 @@ export const PendingApproval: Story = {
271271
type: 'successful',
272272
changedFiles: [],
273273
ref: '123',
274+
nextSteps: [],
274275
},
275276
'migration-2': {
276277
name: 'migration-2',
277278
type: 'successful',
278279
changedFiles: [{ path: 'foo.txt', type: 'CREATE' }],
279280
ref: '124',
281+
nextSteps: ['Check something: https://nx.dev/docs', 'Check another thing: https://nx.dev/docs'],
280282
},
281283
},
282284
targetVersion: '20.3.2',

packages/nx/src/command-line/migrate/migrate-ui-api.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export type SuccessfulMigration = {
2727
name: string;
2828
changedFiles: Omit<FileChange, 'content'>[];
2929
ref: string;
30+
nextSteps?: string[];
3031
};
3132

3233
export type FailedMigration = {
@@ -135,7 +136,7 @@ export async function runSingleMigration(
135136
// 2. Bundled into Console, so the version is fixed to what we build Console with.
136137
const updatedMigrateModule = await import('./migrate.js');
137138

138-
const { changes: fileChanges } =
139+
const { changes: fileChanges, nextSteps } =
139140
await updatedMigrateModule.runNxOrAngularMigration(
140141
workspacePath,
141142
migration,
@@ -159,7 +160,8 @@ export async function runSingleMigration(
159160
path: change.path,
160161
type: change.type,
161162
})),
162-
gitRefAfter
163+
gitRefAfter,
164+
nextSteps
163165
)
164166
);
165167

@@ -234,7 +236,8 @@ export function modifyMigrationsJsonMetadata(
234236
export function addSuccessfulMigration(
235237
id: string,
236238
fileChanges: Omit<FileChange, 'content'>[],
237-
ref: string
239+
ref: string,
240+
nextSteps: string[]
238241
) {
239242
return (
240243
migrationsJsonMetadata: MigrationsJsonMetadata
@@ -250,6 +253,7 @@ export function addSuccessfulMigration(
250253
name: id,
251254
changedFiles: fileChanges,
252255
ref,
256+
nextSteps,
253257
},
254258
};
255259
return copied;

0 commit comments

Comments
 (0)