Ram2005 commited on
Commit
1222f44
·
verified ·
1 Parent(s): b40aaec

Upload frontend/src/components/EntityDetail.jsx

Browse files
frontend/src/components/EntityDetail.jsx CHANGED
@@ -1,5 +1,5 @@
1
- import React from 'react'
2
- import { X } from 'lucide-react'
3
 
4
  const ENTITY_COLORS = {
5
  startup: '#3B82F6', sme: '#10B981', college_ecell: '#FBBF24',
@@ -19,6 +19,125 @@ function formatFunding(v) {
19
  return `₹${(v/100000).toFixed(0)} L`
20
  }
21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  export default function EntityDetail({ entity, onClose, onEntityClick, isMobile }) {
23
  if (!entity) return null
24
 
@@ -78,6 +197,12 @@ export default function EntityDetail({ entity, onClose, onEntityClick, isMobile
78
 
79
  {/* Scrollable body */}
80
  <div className="flex-1 overflow-y-auto px-5 py-4 space-y-4 scrollbar-thin">
 
 
 
 
 
 
81
  {/* Description */}
82
  {e.description && (
83
  <p className="text-sm text-atlas-muted leading-relaxed">{e.description}</p>
@@ -104,24 +229,12 @@ export default function EntityDetail({ entity, onClose, onEntityClick, isMobile
104
 
105
  {/* Additional Info */}
106
  <div className="space-y-2">
107
- {e.stage && (
108
- <InfoRow label="Stage" value={e.stage.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase())} />
109
- )}
110
- {e.business_model && (
111
- <InfoRow label="Business Model" value={e.business_model.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase())} />
112
- )}
113
- {e.dpiit_category && (
114
- <InfoRow label="DPIIT Category" value={e.dpiit_category} />
115
- )}
116
- {e.college_name && (
117
- <InfoRow label="College" value={e.college_name} />
118
- )}
119
- {e.funding_stage && (
120
- <InfoRow label="Funding Stage" value={e.funding_stage} />
121
- )}
122
- {e.valuation_usd && (
123
- <InfoRow label="Valuation" value={`$${(e.valuation_usd / 1e9).toFixed(1)}B`} />
124
- )}
125
  </div>
126
 
127
  {/* Investors */}
@@ -136,17 +249,8 @@ export default function EntityDetail({ entity, onClose, onEntityClick, isMobile
136
  </div>
137
  )}
138
 
139
- {/* Links */}
140
- <div className="space-y-1.5">
141
- <h4 className="text-xs font-semibold text-atlas-muted uppercase tracking-wider">Links</h4>
142
- {e.website && <LinkRow icon="🌐" label="Website" url={e.website} />}
143
- {e.linkedin_url && <LinkRow icon="💼" label="LinkedIn" url={e.linkedin_url} />}
144
- {e.twitter_url && <LinkRow icon="𝕏" label="Twitter" url={e.twitter_url} />}
145
- {e.instagram_url && <LinkRow icon="📸" label="Instagram" url={e.instagram_url} />}
146
- {!e.website && !e.linkedin_url && !e.twitter_url && !e.instagram_url && (
147
- <p className="text-xs text-atlas-muted/50">No links available</p>
148
- )}
149
- </div>
150
 
151
  {/* Nearby Entities */}
152
  {nearby.length > 0 && (
@@ -207,14 +311,3 @@ function InfoRow({ label, value }) {
207
  </div>
208
  )
209
  }
210
-
211
- function LinkRow({ icon, label, url }) {
212
- return (
213
- <a href={url} target="_blank" rel="noopener noreferrer"
214
- className="flex items-center gap-2 text-sm text-brand-500 hover:text-brand-400 transition-colors">
215
- <span>{icon}</span>
216
- <span className="underline underline-offset-2">{label}</span>
217
- <span className="text-atlas-muted/40">↗</span>
218
- </a>
219
- )
220
- }
 
1
+ import React, { useState } from 'react'
2
+ import { X, Globe, ExternalLink } from 'lucide-react'
3
 
4
  const ENTITY_COLORS = {
5
  startup: '#3B82F6', sme: '#10B981', college_ecell: '#FBBF24',
 
19
  return `₹${(v/100000).toFixed(0)} L`
20
  }
21
 
22
+ const SOCIAL_CONFIG = {
23
+ linkedin: { icon: '💼', color: '#0A66C2', label: 'LinkedIn' },
24
+ twitter: { icon: '𝕏', color: '#000000', label: 'X (Twitter)' },
25
+ instagram: { icon: '📸', color: '#E4405F', label: 'Instagram' },
26
+ facebook: { icon: '📘', color: '#1877F2', label: 'Facebook' },
27
+ website: { icon: '🌐', color: '#6366F1', label: 'Website' },
28
+ crunchbase: { icon: 'CB', color: '#0288D1', label: 'Crunchbase' },
29
+ tracxn: { icon: 'TX', color: '#FF6F00', label: 'Tracxn' },
30
+ google: { icon: '🔍', color: '#4285F4', label: 'Google Search' },
31
+ }
32
+
33
+ function SocialLinksGrid({ entity }) {
34
+ const links = []
35
+ const name = entity.name
36
+
37
+ if (entity.linkedin_url) links.push({ ...SOCIAL_CONFIG.linkedin, url: entity.linkedin_url, verified: true })
38
+ if (entity.twitter_url) links.push({ ...SOCIAL_CONFIG.twitter, url: entity.twitter_url, verified: true })
39
+ if (entity.instagram_url) links.push({ ...SOCIAL_CONFIG.instagram, url: entity.instagram_url, verified: true })
40
+ if (entity.facebook_url) links.push({ ...SOCIAL_CONFIG.facebook, url: entity.facebook_url, verified: true })
41
+ if (entity.website) links.push({ ...SOCIAL_CONFIG.website, url: entity.website, verified: true })
42
+
43
+ // Search links (always available)
44
+ const slug = name.toLowerCase().replace(/\s+/g, '-').replace('.', '')
45
+ const encName = encodeURIComponent(name)
46
+ if (!entity.linkedin_url) links.push({ ...SOCIAL_CONFIG.linkedin, url: `https://www.linkedin.com/search/results/companies/?keywords=${encName}`, search: true })
47
+ if (!entity.twitter_url) links.push({ ...SOCIAL_CONFIG.twitter, url: `https://x.com/search?q=${encName}&src=typed_query`, search: true })
48
+ if (!entity.instagram_url) links.push({ ...SOCIAL_CONFIG.instagram, url: `https://www.instagram.com/${slug}`, search: true })
49
+ if (!entity.website) links.push({ ...SOCIAL_CONFIG.website, url: `https://www.google.com/search?q=${encName}+company`, search: true })
50
+ links.push({ ...SOCIAL_CONFIG.crunchbase, url: `https://www.crunchbase.com/organization/${slug}`, search: true })
51
+ links.push({ ...SOCIAL_CONFIG.tracxn, url: `https://tracxn.com/d/companies/${slug}/`, search: true })
52
+ links.push({ ...SOCIAL_CONFIG.google, url: `https://www.google.com/search?q=${encName}+startup+India+news+2024+2025`, search: true })
53
+
54
+ return (
55
+ <div className="grid grid-cols-4 gap-1.5">
56
+ {links.map(l => (
57
+ <a key={l.label + l.url}
58
+ href={l.url}
59
+ target="_blank"
60
+ rel="noopener noreferrer"
61
+ title={`${l.label}${l.search ? ' (search)' : ''}`}
62
+ className="flex flex-col items-center gap-1 p-2 rounded-lg hover:bg-atlas-surface transition-colors group"
63
+ >
64
+ <span className="text-lg" style={{ color: l.color }}>{l.icon}</span>
65
+ <span className="text-[9px] text-atlas-muted group-hover:text-atlas-text text-center leading-tight">{l.label}</span>
66
+ {l.verified && <span className="w-1 h-1 rounded-full bg-emerald-400" title="Verified" />}
67
+ </a>
68
+ ))}
69
+ </div>
70
+ )
71
+ }
72
+
73
+ function AIAnalysisSection({ entity }) {
74
+ const [analysis, setAnalysis] = useState(null)
75
+ const [loading, setLoading] = useState(false)
76
+
77
+ const runAnalysis = async () => {
78
+ setLoading(true)
79
+ try {
80
+ const resp = await fetch('/api/agent/analyze-startup', {
81
+ method: 'POST',
82
+ headers: { 'Content-Type': 'application/json' },
83
+ body: JSON.stringify({
84
+ company_name: entity.name,
85
+ sector: Array.isArray(entity.sectors) ? entity.sectors[0] : entity.dpiit_category || '',
86
+ city: entity.city || '',
87
+ }),
88
+ })
89
+ const data = await resp.json()
90
+ setAnalysis(data)
91
+ } catch (e) {
92
+ setAnalysis({ summary: 'Analysis temporarily unavailable. Try again later.', latest_news: [], confidence: 'low' })
93
+ }
94
+ setLoading(false)
95
+ }
96
+
97
+ return (
98
+ <div className="border-t border-atlas-border/30 pt-3">
99
+ {!analysis && (
100
+ <button onClick={runAnalysis} disabled={loading}
101
+ className="w-full flex items-center justify-center gap-2 py-2 rounded-lg bg-brand-500/10 text-brand-400 hover:bg-brand-500/20 transition-colors text-xs font-medium">
102
+ {loading ? (
103
+ <><span className="w-3 h-3 border-2 border-brand-400 border-t-transparent rounded-full animate-spin" /> Analyzing...</>
104
+ ) : (
105
+ <><span>🤖</span> AI Web Analysis — Latest News & Trends</>
106
+ )}
107
+ </button>
108
+ )}
109
+ {analysis && (
110
+ <div className="space-y-2">
111
+ <div className="flex items-center gap-2 mb-1">
112
+ <span className="text-xs font-semibold text-atlas-muted">🤖 AI Analysis</span>
113
+ <span className={`text-[10px] px-1.5 py-0.5 rounded-full ${
114
+ analysis.confidence === 'high' ? 'bg-emerald-500/15 text-emerald-300' :
115
+ analysis.confidence === 'medium' ? 'bg-amber-500/15 text-amber-300' :
116
+ 'bg-red-500/15 text-red-300'
117
+ }`}>{analysis.confidence} confidence</span>
118
+ </div>
119
+ <p className="text-xs text-atlas-muted leading-relaxed">{analysis.summary}</p>
120
+ {analysis.latest_news?.length > 0 && (
121
+ <div className="space-y-1.5">
122
+ <span className="text-[10px] font-semibold text-atlas-muted/70 uppercase">Latest News</span>
123
+ {analysis.latest_news.slice(0, 3).map((n, i) => (
124
+ <a key={i} href={n.url} target="_blank" rel="noopener noreferrer"
125
+ className="block text-xs text-brand-400 hover:text-brand-300 hover:bg-brand-500/5 rounded px-2 py-1.5 transition-colors truncate">
126
+ • {n.title}
127
+ </a>
128
+ ))}
129
+ </div>
130
+ )}
131
+ <button onClick={() => setAnalysis(null)}
132
+ className="text-[10px] text-atlas-muted/60 hover:text-atlas-muted transition-colors">
133
+ ↻ Run again
134
+ </button>
135
+ </div>
136
+ )}
137
+ </div>
138
+ )
139
+ }
140
+
141
  export default function EntityDetail({ entity, onClose, onEntityClick, isMobile }) {
142
  if (!entity) return null
143
 
 
197
 
198
  {/* Scrollable body */}
199
  <div className="flex-1 overflow-y-auto px-5 py-4 space-y-4 scrollbar-thin">
200
+ {/* Social Media Links */}
201
+ <div>
202
+ <h4 className="text-xs font-semibold text-atlas-muted uppercase tracking-wider mb-2">Social & Profiles</h4>
203
+ <SocialLinksGrid entity={e} />
204
+ </div>
205
+
206
  {/* Description */}
207
  {e.description && (
208
  <p className="text-sm text-atlas-muted leading-relaxed">{e.description}</p>
 
229
 
230
  {/* Additional Info */}
231
  <div className="space-y-2">
232
+ {e.stage && <InfoRow label="Stage" value={e.stage.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase())} />}
233
+ {e.business_model && <InfoRow label="Business Model" value={e.business_model.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase())} />}
234
+ {e.dpiit_category && <InfoRow label="DPIIT Category" value={e.dpiit_category} />}
235
+ {e.college_name && <InfoRow label="College" value={e.college_name} />}
236
+ {e.funding_stage && <InfoRow label="Funding Stage" value={e.funding_stage} />}
237
+ {e.valuation_usd && <InfoRow label="Valuation" value={`$${(e.valuation_usd / 1e9).toFixed(1)}B`} />}
 
 
 
 
 
 
 
 
 
 
 
 
238
  </div>
239
 
240
  {/* Investors */}
 
249
  </div>
250
  )}
251
 
252
+ {/* AI Web Analysis */}
253
+ <AIAnalysisSection entity={e} />
 
 
 
 
 
 
 
 
 
254
 
255
  {/* Nearby Entities */}
256
  {nearby.length > 0 && (
 
311
  </div>
312
  )
313
  }