feat(graph): show next steps for successful migrations (#30934)
This PR updates Migration UI to display "next steps" when they are provided by a migration. This works by writing `nextSteps` into the Nx Console meta in `migrations.json`. If the `nextSteps` is missing or it's empty, then nothing will be shown. <img width="1555" alt="Screenshot 2025-04-29 at 5 16 49 PM" src="https://github.com/user-attachments/assets/88491632-9b33-421a-887a-b6fbb5676098" /> See: https://www.loom.com/share/c0a4a7dce9df46b5b023fce5e0a3bd2f
This commit is contained in:
parent
9dcab79b10
commit
dcef5c7cf2
@ -12,7 +12,13 @@ import {
|
||||
PlayIcon,
|
||||
} from '@heroicons/react/24/outline';
|
||||
import { Pill } from '@nx/graph-internal/ui-project-details';
|
||||
import { useState, forwardRef, useImperativeHandle, useEffect } from 'react';
|
||||
import {
|
||||
useState,
|
||||
forwardRef,
|
||||
useImperativeHandle,
|
||||
useEffect,
|
||||
type ReactNode,
|
||||
} from 'react';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
|
||||
export interface MigrationCardHandle {
|
||||
@ -21,6 +27,30 @@ export interface MigrationCardHandle {
|
||||
toggle: () => void;
|
||||
}
|
||||
|
||||
function convertUrlsToLinks(text: string): ReactNode[] {
|
||||
const urlRegex = /(https?:\/\/[^\s]+)/g;
|
||||
const parts = text.split(urlRegex);
|
||||
const urls = text.match(urlRegex) || [];
|
||||
const result: ReactNode[] = [];
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
if (urls[i - 1]) {
|
||||
result.push(
|
||||
<a
|
||||
key={i}
|
||||
href={urls[i - 1]}
|
||||
target="_blank"
|
||||
className="text-blue-500 hover:underline"
|
||||
>
|
||||
{urls[i - 1]}
|
||||
</a>
|
||||
);
|
||||
} else if (parts[i]) {
|
||||
result.push(parts[i]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export const MigrationCard = forwardRef<
|
||||
MigrationCardHandle,
|
||||
{
|
||||
@ -200,6 +230,23 @@ export const MigrationCard = forwardRef<
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{succeeded && migrationResult?.nextSteps?.length ? (
|
||||
<div className="pt-2">
|
||||
<div className="my-2 border-t border-slate-200 dark:border-slate-700/60" />
|
||||
<span className="pb-2 text-sm font-bold">
|
||||
More Information & Next Steps
|
||||
</span>
|
||||
<ul className="list-inside list-disc pl-2">
|
||||
{migrationResult?.nextSteps.map((step, idx) => (
|
||||
<li key={idx} className="text-sm">
|
||||
{convertUrlsToLinks(step)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<p></p>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div className="mt-4 flex justify-end gap-2">
|
||||
<button
|
||||
onClick={() => onViewImplementation()}
|
||||
@ -266,7 +313,7 @@ export const MigrationCard = forwardRef<
|
||||
onFileClick(file);
|
||||
}}
|
||||
>
|
||||
{file.path}
|
||||
<code>{file.path}</code>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
|
||||
@ -242,7 +242,7 @@ export const PendingApproval: Story = {
|
||||
{
|
||||
id: 'migration-1',
|
||||
name: 'migration-1',
|
||||
description: 'Migrate 1',
|
||||
description: 'This is a test migration',
|
||||
version: '1.0.0',
|
||||
package: 'nx',
|
||||
implementation: './src/migrations/migration-1.ts',
|
||||
@ -250,7 +250,7 @@ export const PendingApproval: Story = {
|
||||
{
|
||||
id: 'migration-2',
|
||||
name: 'migration-2',
|
||||
description: 'Migrate 2',
|
||||
description: 'This is a test migration',
|
||||
version: '1.0.1',
|
||||
package: '@nx/react',
|
||||
implementation: './src/migrations/migration-2.ts',
|
||||
@ -271,12 +271,17 @@ export const PendingApproval: Story = {
|
||||
type: 'successful',
|
||||
changedFiles: [],
|
||||
ref: '123',
|
||||
nextSteps: [],
|
||||
},
|
||||
'migration-2': {
|
||||
name: 'migration-2',
|
||||
type: 'successful',
|
||||
changedFiles: [{ path: 'foo.txt', type: 'CREATE' }],
|
||||
ref: '124',
|
||||
nextSteps: [
|
||||
'Check something: https://nx.dev/docs',
|
||||
'Check another thing: https://nx.dev/docs',
|
||||
],
|
||||
},
|
||||
},
|
||||
targetVersion: '20.3.2',
|
||||
|
||||
@ -27,6 +27,7 @@ export type SuccessfulMigration = {
|
||||
name: string;
|
||||
changedFiles: Omit<FileChange, 'content'>[];
|
||||
ref: string;
|
||||
nextSteps?: string[];
|
||||
};
|
||||
|
||||
export type FailedMigration = {
|
||||
@ -135,7 +136,7 @@ export async function runSingleMigration(
|
||||
// 2. Bundled into Console, so the version is fixed to what we build Console with.
|
||||
const updatedMigrateModule = await import('./migrate.js');
|
||||
|
||||
const { changes: fileChanges } =
|
||||
const { changes: fileChanges, nextSteps } =
|
||||
await updatedMigrateModule.runNxOrAngularMigration(
|
||||
workspacePath,
|
||||
migration,
|
||||
@ -159,7 +160,8 @@ export async function runSingleMigration(
|
||||
path: change.path,
|
||||
type: change.type,
|
||||
})),
|
||||
gitRefAfter
|
||||
gitRefAfter,
|
||||
nextSteps
|
||||
)
|
||||
);
|
||||
|
||||
@ -234,7 +236,8 @@ export function modifyMigrationsJsonMetadata(
|
||||
export function addSuccessfulMigration(
|
||||
id: string,
|
||||
fileChanges: Omit<FileChange, 'content'>[],
|
||||
ref: string
|
||||
ref: string,
|
||||
nextSteps: string[]
|
||||
) {
|
||||
return (
|
||||
migrationsJsonMetadata: MigrationsJsonMetadata
|
||||
@ -250,6 +253,7 @@ export function addSuccessfulMigration(
|
||||
name: id,
|
||||
changedFiles: fileChanges,
|
||||
ref,
|
||||
nextSteps,
|
||||
},
|
||||
};
|
||||
return copied;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user