AgentsView.tsx 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. import React from 'react';
  2. import { useLanguage } from '../../contexts/LanguageContext';
  3. import { useNavigate } from 'react-router-dom';
  4. import { Search, Plus, MoreHorizontal, MessageSquare } from 'lucide-react';
  5. import { motion, AnimatePresence } from 'framer-motion';
  6. import { cn } from '../../src/utils/cn';
  7. // Mock data based on the provided design
  8. interface AgentMock {
  9. id: string;
  10. name: string;
  11. description: string;
  12. status: 'running' | 'stopped';
  13. updatedAt: string;
  14. iconEmoji: string;
  15. iconBgClass: string;
  16. path?: string;
  17. }
  18. const mockAgents: AgentMock[] = [
  19. {
  20. id: 'assessment',
  21. name: 'assessmentTitle',
  22. description: 'assessmentDesc',
  23. status: 'running',
  24. updatedAt: 'agent1Time',
  25. iconEmoji: '📋',
  26. iconBgClass: 'bg-blue-50',
  27. path: '/assessment'
  28. }
  29. ];
  30. export const AgentsView: React.FC = () => {
  31. const { t } = useLanguage();
  32. const navigate = useNavigate();
  33. return (
  34. <div className="flex flex-col h-full bg-[#f4f7fb] overflow-hidden">
  35. {/* Header Area */}
  36. <div className="px-8 pt-8 pb-6 flex items-start justify-between shrink-0">
  37. <div>
  38. <h1 className="text-[22px] font-bold text-slate-900 leading-tight">
  39. {t('agentTitle')}
  40. </h1>
  41. <p className="text-[14px] text-slate-500 mt-1">{t('agentDesc')}</p>
  42. </div>
  43. <div className="flex items-center gap-4">
  44. <div className="relative w-64">
  45. <Search className="absolute text-slate-400 left-3 top-1/2 -translate-y-1/2" size={16} />
  46. <input
  47. type="text"
  48. placeholder={t('searchAgent')}
  49. className="w-full h-10 pl-10 pr-4 bg-white border border-slate-200 rounded-lg focus:bg-white focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 outline-none transition-all text-sm font-medium"
  50. />
  51. </div>
  52. <button className="flex items-center gap-2 px-5 h-10 bg-blue-600 hover:bg-blue-700 text-white rounded-lg shadow-sm shadow-blue-100 transition-all font-semibold text-sm active:scale-95">
  53. <Plus size={18} />
  54. <span>{t('createAgent')}</span>
  55. </button>
  56. </div>
  57. </div>
  58. {/* Content Area */}
  59. <div className="px-8 pb-8 flex-1 overflow-y-auto">
  60. <div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6 max-w-[1600px] mx-auto">
  61. <AnimatePresence>
  62. {mockAgents.map((agent) => (
  63. <motion.div
  64. key={agent.id}
  65. layout
  66. initial={{ opacity: 0, y: 10 }}
  67. animate={{ opacity: 1, y: 0 }}
  68. className={cn(
  69. "bg-white rounded-2xl p-6 shadow-sm border border-slate-100 hover:shadow-md transition-all group flex flex-col h-[220px]",
  70. agent.path && "cursor-pointer hover:border-blue-200"
  71. )}
  72. onClick={() => {
  73. if (agent.path) {
  74. navigate(agent.path);
  75. }
  76. }}
  77. >
  78. {/* Top layer */}
  79. <div className="flex items-center justify-between mb-4">
  80. <div className={`w-12 h-12 flex items-center justify-center rounded-xl ${agent.iconBgClass} text-2xl`}>
  81. {agent.iconEmoji}
  82. </div>
  83. <div className="flex items-center gap-3">
  84. {/* Status Badge */}
  85. {agent.status === 'running' ? (
  86. <div className="px-2.5 py-1 text-[12px] font-semibold text-emerald-600 bg-emerald-50 rounded-full border border-emerald-100/50 flex flex-row items-center justify-center">
  87. {t('statusRunning')}
  88. </div>
  89. ) : (
  90. <div className="px-2.5 py-1 text-[12px] font-semibold text-slate-500 bg-slate-50 rounded-full border border-slate-100 flex flex-row items-center justify-center">
  91. {t('statusStopped')}
  92. </div>
  93. )}
  94. {/* Options button */}
  95. <button className="text-slate-400 hover:text-slate-600 transition-colors">
  96. <MoreHorizontal size={20} />
  97. </button>
  98. </div>
  99. </div>
  100. {/* Middle layer */}
  101. <div className="flex-1">
  102. <h3 className="font-bold text-slate-800 text-[17px] mb-2 leading-tight">
  103. {t(agent.name as any)}
  104. </h3>
  105. <p className="text-[13px] text-slate-500 leading-relaxed line-clamp-2">
  106. {t(agent.description as any)}
  107. </p>
  108. </div>
  109. {/* Bottom layer */}
  110. <div className="mt-4 pt-4 border-t border-slate-50 flex items-center justify-between">
  111. <span className="text-[12px] font-medium text-slate-400">
  112. {t('updatedAtPrefix')}{t(agent.updatedAt as any)}
  113. </span>
  114. <button
  115. onClick={(e) => {
  116. e.stopPropagation();
  117. if (agent.path) {
  118. navigate(agent.path);
  119. }
  120. }}
  121. className="flex items-center justify-center gap-1.5 px-3 py-1.5 text-blue-600 bg-blue-50 hover:bg-blue-100 rounded-lg transition-colors"
  122. >
  123. <MessageSquare size={14} className="text-blue-500" />
  124. <span className="text-[13px] font-bold">{t('btnChat')}</span>
  125. </button>
  126. </div>
  127. </motion.div>
  128. ))}
  129. </AnimatePresence>
  130. </div>
  131. </div>
  132. </div>
  133. );
  134. };