AgentsView.tsx 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  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. id: 'data-analyst',
  31. name: 'agent1Name',
  32. description: 'agent1Desc',
  33. status: 'running',
  34. updatedAt: 'agent1Time',
  35. iconEmoji: '📊',
  36. iconBgClass: 'bg-emerald-50'
  37. },
  38. {
  39. id: 'code-review',
  40. name: 'agent2Name',
  41. description: 'agent2Desc',
  42. status: 'running',
  43. updatedAt: 'agent2Time',
  44. iconEmoji: '💻',
  45. iconBgClass: 'bg-indigo-50'
  46. },
  47. {
  48. id: 'paper-polisher',
  49. name: 'agent3Name',
  50. description: 'agent3Desc',
  51. status: 'stopped',
  52. updatedAt: 'agent3Time',
  53. iconEmoji: '✍️',
  54. iconBgClass: 'bg-amber-50'
  55. },
  56. {
  57. id: 'legal-consultant',
  58. name: 'agent4Name',
  59. description: 'agent4Desc',
  60. status: 'running',
  61. updatedAt: 'agent4Time',
  62. iconEmoji: '⚖️',
  63. iconBgClass: 'bg-rose-50'
  64. },
  65. {
  66. id: 'market-researcher',
  67. name: 'agent5Name',
  68. description: 'agent5Desc',
  69. status: 'running',
  70. updatedAt: 'agent5Time',
  71. iconEmoji: '📈',
  72. iconBgClass: 'bg-cyan-50'
  73. }
  74. ];
  75. export const AgentsView: React.FC = () => {
  76. const { t } = useLanguage();
  77. const navigate = useNavigate();
  78. return (
  79. <div className="flex flex-col h-full bg-[#f4f7fb] overflow-hidden">
  80. {/* Header Area */}
  81. <div className="px-8 pt-8 pb-6 flex items-start justify-between shrink-0">
  82. <div>
  83. <h1 className="text-[22px] font-bold text-slate-900 leading-tight">
  84. {t('agentTitle')}
  85. </h1>
  86. <p className="text-[14px] text-slate-500 mt-1">{t('agentDesc')}</p>
  87. </div>
  88. <div className="flex items-center gap-4">
  89. <div className="relative w-64">
  90. <Search className="absolute text-slate-400 left-3 top-1/2 -translate-y-1/2" size={16} />
  91. <input
  92. type="text"
  93. placeholder={t('searchAgent')}
  94. 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"
  95. />
  96. </div>
  97. <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">
  98. <Plus size={18} />
  99. <span>{t('createAgent')}</span>
  100. </button>
  101. </div>
  102. </div>
  103. {/* Content Area */}
  104. <div className="px-8 pb-8 flex-1 overflow-y-auto">
  105. <div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6 max-w-[1600px] mx-auto">
  106. <AnimatePresence>
  107. {mockAgents.map((agent) => (
  108. <motion.div
  109. key={agent.id}
  110. layout
  111. initial={{ opacity: 0, y: 10 }}
  112. animate={{ opacity: 1, y: 0 }}
  113. className={cn(
  114. "bg-white rounded-2xl p-6 shadow-sm border border-slate-100 hover:shadow-md transition-all group flex flex-col h-[220px]",
  115. agent.path && "cursor-pointer hover:border-blue-200"
  116. )}
  117. onClick={() => {
  118. if (agent.path) {
  119. navigate(agent.path);
  120. }
  121. }}
  122. >
  123. {/* Top layer */}
  124. <div className="flex items-center justify-between mb-4">
  125. <div className={`w-12 h-12 flex items-center justify-center rounded-xl ${agent.iconBgClass} text-2xl`}>
  126. {agent.iconEmoji}
  127. </div>
  128. <div className="flex items-center gap-3">
  129. {/* Status Badge */}
  130. {agent.status === 'running' ? (
  131. <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">
  132. {t('statusRunning')}
  133. </div>
  134. ) : (
  135. <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">
  136. {t('statusStopped')}
  137. </div>
  138. )}
  139. {/* Options button */}
  140. <button className="text-slate-400 hover:text-slate-600 transition-colors">
  141. <MoreHorizontal size={20} />
  142. </button>
  143. </div>
  144. </div>
  145. {/* Middle layer */}
  146. <div className="flex-1">
  147. <h3 className="font-bold text-slate-800 text-[17px] mb-2 leading-tight">
  148. {t(agent.name as any)}
  149. </h3>
  150. <p className="text-[13px] text-slate-500 leading-relaxed line-clamp-2">
  151. {t(agent.description as any)}
  152. </p>
  153. </div>
  154. {/* Bottom layer */}
  155. <div className="mt-4 pt-4 border-t border-slate-50 flex items-center justify-between">
  156. <span className="text-[12px] font-medium text-slate-400">
  157. {t('updatedAtPrefix')}{t(agent.updatedAt as any)}
  158. </span>
  159. <button
  160. onClick={(e) => {
  161. e.stopPropagation();
  162. if (agent.path) {
  163. navigate(agent.path);
  164. }
  165. }}
  166. 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"
  167. >
  168. <MessageSquare size={14} className="text-blue-500" />
  169. <span className="text-[13px] font-bold">{t('btnChat')}</span>
  170. </button>
  171. </div>
  172. </motion.div>
  173. ))}
  174. </AnimatePresence>
  175. </div>
  176. </div>
  177. </div>
  178. );
  179. };