feat: Phase I.2 I-UI — 5 new block types (agent_result, approval_request, task_card, workflow_card, knowledge_card) in BlockRenderer, tsc clean
This commit is contained in:
@@ -0,0 +1,25 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import type { MessageBlock } from '@/store/commStore';
|
||||||
|
import { Bot, CheckCircle, XCircle, Loader2 } from 'lucide-react';
|
||||||
|
|
||||||
|
interface AgentResultBlockProps {
|
||||||
|
block: MessageBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AgentResultBlock: React.FC<AgentResultBlockProps> = ({ block }) => {
|
||||||
|
const { agent_name, status, result, run_id } = block.block_data;
|
||||||
|
const statusIcon = status === 'completed' ? <CheckCircle className="w-4 h-4 text-success-500" /> : status === 'failed' ? <XCircle className="w-4 h-4 text-danger-500" /> : <Loader2 className="w-4 h-4 text-primary-500 animate-spin" />;
|
||||||
|
return (
|
||||||
|
<div className="border border-secondary-200 rounded-lg p-3 bg-primary-50">
|
||||||
|
<div className="flex items-center gap-2 mb-2">
|
||||||
|
<Bot className="w-4 h-4 text-primary-600" />
|
||||||
|
<span className="text-sm font-medium text-secondary-900">{agent_name || 'Agent'}</span>
|
||||||
|
{statusIcon}
|
||||||
|
<span className="text-xs text-secondary-500 capitalize">{status}</span>
|
||||||
|
</div>
|
||||||
|
{result && <p className="text-sm text-secondary-700 line-clamp-4">{result}</p>}
|
||||||
|
{run_id && <p className="text-xs text-secondary-400 mt-1">Run: {run_id.slice(0, 8)}</p>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default AgentResultBlock;
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import type { MessageBlock } from '@/store/commStore';
|
||||||
|
import { ShieldCheck, Check, X } from 'lucide-react';
|
||||||
|
|
||||||
|
interface ApprovalRequestBlockProps {
|
||||||
|
block: MessageBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ApprovalRequestBlock: React.FC<ApprovalRequestBlockProps> = ({ block }) => {
|
||||||
|
const { title, description, approval_id, status } = block.block_data;
|
||||||
|
return (
|
||||||
|
<div className="border-2 border-warning-200 rounded-lg p-3 bg-warning-50">
|
||||||
|
<div className="flex items-center gap-2 mb-2">
|
||||||
|
<ShieldCheck className="w-4 h-4 text-warning-600" />
|
||||||
|
<span className="text-sm font-medium text-secondary-900">{title || 'Approval Required'}</span>
|
||||||
|
{status && <span className="text-xs px-2 py-0.5 rounded-full bg-warning-100 text-warning-700 capitalize">{status}</span>}
|
||||||
|
</div>
|
||||||
|
{description && <p className="text-sm text-secondary-700 mb-2">{description}</p>}
|
||||||
|
{status === 'pending' && (
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<button className="inline-flex items-center gap-1 px-3 py-1.5 rounded-lg bg-success-600 text-white text-sm hover:bg-success-700 min-h-touch">
|
||||||
|
<Check className="w-3.5 h-3.5" /> Approve
|
||||||
|
</button>
|
||||||
|
<button className="inline-flex items-center gap-1 px-3 py-1.5 rounded-lg bg-danger-600 text-white text-sm hover:bg-danger-700 min-h-touch">
|
||||||
|
<X className="w-3.5 h-3.5" /> Reject
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{approval_id && <p className="text-xs text-secondary-400 mt-1">ID: {approval_id.slice(0, 8)}</p>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default ApprovalRequestBlock;
|
||||||
@@ -9,6 +9,11 @@ import FileBlock from './FileBlock';
|
|||||||
import ActionCardBlock from './ActionCardBlock';
|
import ActionCardBlock from './ActionCardBlock';
|
||||||
import ContactCardBlock from './ContactCardBlock';
|
import ContactCardBlock from './ContactCardBlock';
|
||||||
import MiniAppBlock from './MiniAppBlock';
|
import MiniAppBlock from './MiniAppBlock';
|
||||||
|
import AgentResultBlock from './AgentResultBlock';
|
||||||
|
import ApprovalRequestBlock from './ApprovalRequestBlock';
|
||||||
|
import TaskCardBlock from './TaskCardBlock';
|
||||||
|
import WorkflowCardBlock from './WorkflowCardBlock';
|
||||||
|
import KnowledgeCardBlock from './KnowledgeCardBlock';
|
||||||
|
|
||||||
interface BlockRendererProps {
|
interface BlockRendererProps {
|
||||||
blocks: MessageBlock[];
|
blocks: MessageBlock[];
|
||||||
@@ -52,6 +57,16 @@ const BlockRenderer: React.FC<BlockRendererProps> = ({ blocks }) => {
|
|||||||
return <ContactCardBlock block={block} />;
|
return <ContactCardBlock block={block} />;
|
||||||
case 'miniapp':
|
case 'miniapp':
|
||||||
return <MiniAppBlock block={block} />;
|
return <MiniAppBlock block={block} />;
|
||||||
|
case 'agent_result':
|
||||||
|
return <AgentResultBlock block={block} />;
|
||||||
|
case 'approval_request':
|
||||||
|
return <ApprovalRequestBlock block={block} />;
|
||||||
|
case 'task_card':
|
||||||
|
return <TaskCardBlock block={block} />;
|
||||||
|
case 'workflow_card':
|
||||||
|
return <WorkflowCardBlock block={block} />;
|
||||||
|
case 'knowledge_card':
|
||||||
|
return <KnowledgeCardBlock block={block} />;
|
||||||
default:
|
default:
|
||||||
// Fallback for unknown block types
|
// Fallback for unknown block types
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import type { MessageBlock } from '@/store/commStore';
|
||||||
|
import { BookOpen, ExternalLink } from 'lucide-react';
|
||||||
|
|
||||||
|
interface KnowledgeCardBlockProps {
|
||||||
|
block: MessageBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
const KnowledgeCardBlock: React.FC<KnowledgeCardBlockProps> = ({ block }) => {
|
||||||
|
const { title, summary, source_type, url, confidence } = block.block_data;
|
||||||
|
return (
|
||||||
|
<div className="border border-secondary-200 rounded-lg p-3 bg-white">
|
||||||
|
<div className="flex items-center gap-2 mb-2">
|
||||||
|
<BookOpen className="w-4 h-4 text-primary-600" />
|
||||||
|
<span className="text-sm font-medium text-secondary-900">{title || 'Knowledge'}</span>
|
||||||
|
{source_type && <span className="text-xs px-2 py-0.5 rounded-full bg-primary-100 text-primary-700">{source_type}</span>}
|
||||||
|
</div>
|
||||||
|
{summary && <p className="text-sm text-secondary-600 line-clamp-3">{summary}</p>}
|
||||||
|
<div className="flex items-center gap-3 mt-2">
|
||||||
|
{confidence !== undefined && <span className="text-xs text-secondary-400">Confidence: {Math.round(confidence * 100)}%</span>}
|
||||||
|
{url && <a href={url} className="text-xs text-primary-600 hover:underline flex items-center gap-1"><ExternalLink className="w-3 h-3" /> Open</a>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default KnowledgeCardBlock;
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import type { MessageBlock } from '@/store/commStore';
|
||||||
|
import { CheckSquare, Clock, User } from 'lucide-react';
|
||||||
|
|
||||||
|
interface TaskCardBlockProps {
|
||||||
|
block: MessageBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TaskCardBlock: React.FC<TaskCardBlockProps> = ({ block }) => {
|
||||||
|
const { title, assignee, due_date, priority, status, task_id } = block.block_data;
|
||||||
|
const priorityColor = priority === 'high' ? 'text-danger-600' : priority === 'medium' ? 'text-warning-600' : 'text-secondary-500';
|
||||||
|
return (
|
||||||
|
<div className="border border-secondary-200 rounded-lg p-3 bg-white">
|
||||||
|
<div className="flex items-center gap-2 mb-2">
|
||||||
|
<CheckSquare className="w-4 h-4 text-primary-600" />
|
||||||
|
<span className="text-sm font-medium text-secondary-900">{title || 'Task'}</span>
|
||||||
|
{status && <span className="text-xs px-2 py-0.5 rounded-full bg-secondary-100 text-secondary-600 capitalize">{status}</span>}
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3 text-xs text-secondary-500">
|
||||||
|
{assignee && <span className="flex items-center gap-1"><User className="w-3 h-3" /> {assignee}</span>}
|
||||||
|
{due_date && <span className="flex items-center gap-1"><Clock className="w-3 h-3" /> {due_date}</span>}
|
||||||
|
{priority && <span className={priorityColor}>● {priority}</span>}
|
||||||
|
</div>
|
||||||
|
{task_id && <p className="text-xs text-secondary-400 mt-1">Task: {task_id.slice(0, 8)}</p>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default TaskCardBlock;
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import type { MessageBlock } from '@/store/commStore';
|
||||||
|
import { Workflow as WorkflowIcon, CheckCircle, XCircle, Loader2 } from 'lucide-react';
|
||||||
|
|
||||||
|
interface WorkflowCardBlockProps {
|
||||||
|
block: MessageBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
const WorkflowCardBlock: React.FC<WorkflowCardBlockProps> = ({ block }) => {
|
||||||
|
const { workflow_name, status, current_step, total_steps, instance_id } = block.block_data;
|
||||||
|
const statusIcon = status === 'completed' ? <CheckCircle className="w-4 h-4 text-success-500" /> : status === 'failed' ? <XCircle className="w-4 h-4 text-danger-500" /> : <Loader2 className="w-4 h-4 text-primary-500 animate-spin" />;
|
||||||
|
return (
|
||||||
|
<div className="border border-secondary-200 rounded-lg p-3 bg-secondary-50">
|
||||||
|
<div className="flex items-center gap-2 mb-2">
|
||||||
|
<WorkflowIcon className="w-4 h-4 text-primary-600" />
|
||||||
|
<span className="text-sm font-medium text-secondary-900">{workflow_name || 'Workflow'}</span>
|
||||||
|
{statusIcon}
|
||||||
|
<span className="text-xs text-secondary-500 capitalize">{status}</span>
|
||||||
|
</div>
|
||||||
|
{current_step !== undefined && total_steps !== undefined && (
|
||||||
|
<div className="w-full bg-secondary-200 rounded-full h-1.5 mb-1">
|
||||||
|
<div className="bg-primary-600 h-1.5 rounded-full" style={{ width: `${(current_step / total_steps) * 100}%` }} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<p className="text-xs text-secondary-500">Step {current_step || 0} / {total_steps || 0}</p>
|
||||||
|
{instance_id && <p className="text-xs text-secondary-400 mt-1">Instance: {instance_id.slice(0, 8)}</p>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default WorkflowCardBlock;
|
||||||
@@ -8,3 +8,8 @@ export { default as FileBlock } from './FileBlock';
|
|||||||
export { default as ActionCardBlock } from './ActionCardBlock';
|
export { default as ActionCardBlock } from './ActionCardBlock';
|
||||||
export { default as ContactCardBlock } from './ContactCardBlock';
|
export { default as ContactCardBlock } from './ContactCardBlock';
|
||||||
export { default as MiniAppBlock } from './MiniAppBlock';
|
export { default as MiniAppBlock } from './MiniAppBlock';
|
||||||
|
export { default as AgentResultBlock } from './AgentResultBlock';
|
||||||
|
export { default as ApprovalRequestBlock } from './ApprovalRequestBlock';
|
||||||
|
export { default as TaskCardBlock } from './TaskCardBlock';
|
||||||
|
export { default as WorkflowCardBlock } from './WorkflowCardBlock';
|
||||||
|
export { default as KnowledgeCardBlock } from './KnowledgeCardBlock';
|
||||||
|
|||||||
Reference in New Issue
Block a user