feat(frontend): add resource-specific confirmation dialog components

Add specialized confirmation UI components for each deeplink import
resource type (Prompt, MCP, Skill).

Components:
- PromptConfirmation: Display prompt name, app, description, and
  content preview with markdown rendering
- McpConfirmation: Show MCP server list with target apps, supports
  batch import display
- SkillConfirmation: Display GitHub repository info with branch and
  directory details

Features:
- Consistent card-based layout with proper spacing
- Sensitive data masking (API keys shown as dots)
- Icon support for providers (ProviderIcon component)
- Badge components for visual status indicators
- Responsive design with proper text overflow handling

Each component focuses on displaying the most relevant information
for users to make informed import decisions.
This commit is contained in:
YoVinchen
2025-11-24 22:43:21 +08:00
parent 7574d049ff
commit 1dc1f86560
3 changed files with 187 additions and 0 deletions
@@ -0,0 +1,71 @@
import { useMemo } from "react";
import { DeepLinkImportRequest } from "../../lib/api/deeplink";
import { decodeBase64Utf8 } from "../../lib/utils/base64";
export function McpConfirmation({
request,
}: {
request: DeepLinkImportRequest;
}) {
const mcpServers = useMemo(() => {
if (!request.config) return null;
try {
const decoded = decodeBase64Utf8(request.config);
const parsed = JSON.parse(decoded);
return parsed.mcpServers || {};
} catch (e) {
console.error("Failed to parse MCP config:", e);
return null;
}
}, [request.config]);
const targetApps = request.apps?.split(",") || [];
return (
<div className="space-y-4">
<h3 className="text-lg font-semibold"> MCP Servers</h3>
<div>
<label className="block text-sm font-medium text-muted-foreground">
</label>
<div className="mt-1 flex gap-2 flex-wrap">
{targetApps.map((app) => (
<span
key={app}
className="px-2 py-1 bg-primary/10 text-primary text-xs rounded capitalize"
>
{app.trim()}
</span>
))}
</div>
</div>
<div>
<label className="block text-sm font-medium text-muted-foreground">
MCP Servers ({Object.keys(mcpServers || {}).length} )
</label>
<div className="mt-1 space-y-2 max-h-64 overflow-auto border rounded p-2 bg-muted/30">
{mcpServers &&
Object.entries(mcpServers).map(([id, spec]: [string, any]) => (
<div key={id} className="p-2 bg-background rounded border">
<div className="font-semibold text-sm">{id}</div>
<div className="text-xs text-muted-foreground mt-1 font-mono truncate">
{spec.command
? `Command: ${spec.command} `
: `URL: ${spec.url} `}
</div>
</div>
))}
</div>
</div>
{request.enabled && (
<div className="text-yellow-600 dark:text-yellow-500 text-sm flex items-center gap-2">
<span></span>
<span></span>
</div>
)}
</div>
);
}
@@ -0,0 +1,60 @@
import { useMemo } from "react";
import { DeepLinkImportRequest } from "../../lib/api/deeplink";
import { decodeBase64Utf8 } from "../../lib/utils/base64";
export function PromptConfirmation({
request,
}: {
request: DeepLinkImportRequest;
}) {
const decodedContent = useMemo(() => {
if (!request.content) return "";
return decodeBase64Utf8(request.content);
}, [request.content]);
return (
<div className="space-y-4">
<h3 className="text-lg font-semibold"></h3>
<div>
<label className="block text-sm font-medium text-muted-foreground">
</label>
<div className="mt-1 text-sm capitalize">{request.app}</div>
</div>
<div>
<label className="block text-sm font-medium text-muted-foreground">
</label>
<div className="mt-1 text-sm">{request.name}</div>
</div>
{request.description && (
<div>
<label className="block text-sm font-medium text-muted-foreground">
</label>
<div className="mt-1 text-sm">{request.description}</div>
</div>
)}
<div>
<label className="block text-sm font-medium text-muted-foreground">
</label>
<pre className="mt-1 max-h-48 overflow-auto bg-muted/50 p-2 rounded text-xs whitespace-pre-wrap border">
{decodedContent.substring(0, 500)}
{decodedContent.length > 500 && "..."}
</pre>
</div>
{request.enabled && (
<div className="text-yellow-600 dark:text-yellow-500 text-sm flex items-center gap-2">
<span></span>
<span></span>
</div>
)}
</div>
);
}
@@ -0,0 +1,56 @@
import { DeepLinkImportRequest } from "../../lib/api/deeplink";
export function SkillConfirmation({
request,
}: {
request: DeepLinkImportRequest;
}) {
return (
<div className="space-y-4">
<h3 className="text-lg font-semibold"> Claude Skill </h3>
<div>
<label className="block text-sm font-medium text-muted-foreground">
GitHub
</label>
<div className="mt-1 text-sm font-mono bg-muted/50 p-2 rounded border">
{request.repo}
</div>
</div>
<div>
<label className="block text-sm font-medium text-muted-foreground">
</label>
<div className="mt-1 text-sm font-mono bg-muted/50 p-2 rounded border">
{request.directory}
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-muted-foreground">
</label>
<div className="mt-1 text-sm">{request.branch || "main"}</div>
</div>
{request.skillsPath && (
<div>
<label className="block text-sm font-medium text-muted-foreground">
Skills
</label>
<div className="mt-1 text-sm">{request.skillsPath}</div>
</div>
)}
</div>
<div className="text-blue-600 dark:text-blue-400 text-sm bg-blue-50 dark:bg-blue-950/30 p-3 rounded border border-blue-200 dark:border-blue-800">
<p> Skill </p>
<p className="mt-1">
Skills Skill
</p>
</div>
</div>
);
}