Álvaro Valenzuela Valdes commited on
Commit ·
1dbaf75
1
Parent(s): 59fcb47
feat: Premium interactive rows, file icons, and enhanced search filtering
Browse files- backend/app/main.py +8 -2
- backend/app/routers/tenders.py +3 -1
- frontend/components/TenderSearch.tsx +27 -13
backend/app/main.py
CHANGED
|
@@ -109,8 +109,14 @@ async def startup_event():
|
|
| 109 |
region="Valparaíso",
|
| 110 |
sector="Cloud",
|
| 111 |
source="Mercado Público",
|
| 112 |
-
items=[
|
| 113 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
)
|
| 115 |
]
|
| 116 |
for t in tenders_to_add:
|
|
|
|
| 109 |
region="Valparaíso",
|
| 110 |
sector="Cloud",
|
| 111 |
source="Mercado Público",
|
| 112 |
+
items=[
|
| 113 |
+
{"name": "Migration Service", "quantity": 1, "unit": "Unit"},
|
| 114 |
+
{"name": "Cloud Storage 100TB", "quantity": 1, "unit": "Year"}
|
| 115 |
+
],
|
| 116 |
+
attachments=[
|
| 117 |
+
{"name": "Bases_Tecnicas.pdf", "url": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"},
|
| 118 |
+
{"name": "Anexo_Economico.docx", "url": "https://calibre-ebook.com/downloads/demos/demo.docx"}
|
| 119 |
+
]
|
| 120 |
)
|
| 121 |
]
|
| 122 |
for t in tenders_to_add:
|
backend/app/routers/tenders.py
CHANGED
|
@@ -29,7 +29,9 @@ async def search_tender_opportunities(
|
|
| 29 |
TenderModel.name.ilike(search_filter),
|
| 30 |
TenderModel.code.ilike(search_filter),
|
| 31 |
TenderModel.description.ilike(search_filter),
|
| 32 |
-
TenderModel.buyer.ilike(search_filter)
|
|
|
|
|
|
|
| 33 |
)
|
| 34 |
)
|
| 35 |
|
|
|
|
| 29 |
TenderModel.name.ilike(search_filter),
|
| 30 |
TenderModel.code.ilike(search_filter),
|
| 31 |
TenderModel.description.ilike(search_filter),
|
| 32 |
+
TenderModel.buyer.ilike(search_filter),
|
| 33 |
+
TenderModel.sector.ilike(search_filter),
|
| 34 |
+
TenderModel.region.ilike(search_filter)
|
| 35 |
)
|
| 36 |
)
|
| 37 |
|
frontend/components/TenderSearch.tsx
CHANGED
|
@@ -177,11 +177,17 @@ export default function TenderSearch({ tenders, onSearch, onAnalyze, forceShowFo
|
|
| 177 |
<tbody className="divide-y divide-white/5">
|
| 178 |
{filteredTenders.map((tender) => (
|
| 179 |
<Fragment key={tender.code}>
|
| 180 |
-
<tr
|
|
|
|
|
|
|
|
|
|
| 181 |
<td className="px-6 py-5">
|
| 182 |
<div className="flex items-center gap-3">
|
| 183 |
<button
|
| 184 |
-
onClick={() =>
|
|
|
|
|
|
|
|
|
|
| 185 |
className={`text-lg transition-all hover:scale-125 ${followedCodes.includes(tender.code) ? 'text-purple-400 drop-shadow-[0_0_8px_rgba(168,85,247,0.4)]' : 'text-slate-600 hover:text-slate-400'}`}
|
| 186 |
>
|
| 187 |
{followedCodes.includes(tender.code) ? "★" : "☆"}
|
|
@@ -191,7 +197,10 @@ export default function TenderSearch({ tenders, onSearch, onAnalyze, forceShowFo
|
|
| 191 |
</td>
|
| 192 |
<td className="px-6 py-5 max-w-xs">
|
| 193 |
<div className="font-semibold text-white group-hover:text-purple-400 transition-colors truncate">{tender.name}</div>
|
| 194 |
-
<div className="
|
|
|
|
|
|
|
|
|
|
| 195 |
</td>
|
| 196 |
<td className="px-6 py-5 text-slate-400 text-xs">{tender.buyer}</td>
|
| 197 |
<td className="px-6 py-5 text-center">
|
|
@@ -209,13 +218,10 @@ export default function TenderSearch({ tenders, onSearch, onAnalyze, forceShowFo
|
|
| 209 |
<td className="px-6 py-5 text-right pr-10">
|
| 210 |
<div className="flex items-center justify-end gap-3">
|
| 211 |
<button
|
| 212 |
-
onClick={() =>
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
</button>
|
| 217 |
-
<button
|
| 218 |
-
onClick={() => onAnalyze(tender)}
|
| 219 |
className="bg-white/10 hover:bg-white/20 text-white text-[11px] font-bold px-4 py-2 rounded-lg transition-all border border-white/10"
|
| 220 |
>
|
| 221 |
Analyze
|
|
@@ -249,9 +255,17 @@ export default function TenderSearch({ tenders, onSearch, onAnalyze, forceShowFo
|
|
| 249 |
<h4 className="text-[10px] font-bold uppercase tracking-widest text-purple-400 mb-3">Resources & Direct Links</h4>
|
| 250 |
<div className="grid gap-3">
|
| 251 |
{tender.attachments?.map((att, i) => (
|
| 252 |
-
<a key={i} href={att.url} target="_blank" className="flex items-center
|
| 253 |
-
<
|
| 254 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 255 |
</a>
|
| 256 |
))}
|
| 257 |
<a
|
|
|
|
| 177 |
<tbody className="divide-y divide-white/5">
|
| 178 |
{filteredTenders.map((tender) => (
|
| 179 |
<Fragment key={tender.code}>
|
| 180 |
+
<tr
|
| 181 |
+
className="hover:bg-white/[0.04] cursor-pointer transition-colors group"
|
| 182 |
+
onClick={() => toggleExpanded(tender.code)}
|
| 183 |
+
>
|
| 184 |
<td className="px-6 py-5">
|
| 185 |
<div className="flex items-center gap-3">
|
| 186 |
<button
|
| 187 |
+
onClick={(e) => {
|
| 188 |
+
e.stopPropagation();
|
| 189 |
+
toggleFollow(tender.code);
|
| 190 |
+
}}
|
| 191 |
className={`text-lg transition-all hover:scale-125 ${followedCodes.includes(tender.code) ? 'text-purple-400 drop-shadow-[0_0_8px_rgba(168,85,247,0.4)]' : 'text-slate-600 hover:text-slate-400'}`}
|
| 192 |
>
|
| 193 |
{followedCodes.includes(tender.code) ? "★" : "☆"}
|
|
|
|
| 197 |
</td>
|
| 198 |
<td className="px-6 py-5 max-w-xs">
|
| 199 |
<div className="font-semibold text-white group-hover:text-purple-400 transition-colors truncate">{tender.name}</div>
|
| 200 |
+
<div className="flex items-center gap-2 mt-1">
|
| 201 |
+
<span className="text-[10px] text-slate-500">{tender.region || "Multiregional"}</span>
|
| 202 |
+
<span className="text-[8px] px-1.5 py-0.5 rounded-md bg-white/5 text-slate-600 border border-white/5 uppercase tracking-tighter">{tender.sector}</span>
|
| 203 |
+
</div>
|
| 204 |
</td>
|
| 205 |
<td className="px-6 py-5 text-slate-400 text-xs">{tender.buyer}</td>
|
| 206 |
<td className="px-6 py-5 text-center">
|
|
|
|
| 218 |
<td className="px-6 py-5 text-right pr-10">
|
| 219 |
<div className="flex items-center justify-end gap-3">
|
| 220 |
<button
|
| 221 |
+
onClick={(e) => {
|
| 222 |
+
e.stopPropagation();
|
| 223 |
+
onAnalyze(tender);
|
| 224 |
+
}}
|
|
|
|
|
|
|
|
|
|
| 225 |
className="bg-white/10 hover:bg-white/20 text-white text-[11px] font-bold px-4 py-2 rounded-lg transition-all border border-white/10"
|
| 226 |
>
|
| 227 |
Analyze
|
|
|
|
| 255 |
<h4 className="text-[10px] font-bold uppercase tracking-widest text-purple-400 mb-3">Resources & Direct Links</h4>
|
| 256 |
<div className="grid gap-3">
|
| 257 |
{tender.attachments?.map((att, i) => (
|
| 258 |
+
<a key={i} href={att.url} target="_blank" className="flex items-center justify-between p-4 rounded-2xl bg-white/[0.03] hover:bg-white/[0.08] border border-white/5 transition-all group/file">
|
| 259 |
+
<div className="flex items-center gap-4">
|
| 260 |
+
<span className="text-2xl">
|
| 261 |
+
{att.name.endsWith('.pdf') ? "📕" : "📘"}
|
| 262 |
+
</span>
|
| 263 |
+
<div className="flex flex-col">
|
| 264 |
+
<span className="text-xs font-bold text-slate-200 group-hover/file:text-white transition-colors">{att.name}</span>
|
| 265 |
+
<span className="text-[10px] text-slate-500 uppercase tracking-tighter">Official Document</span>
|
| 266 |
+
</div>
|
| 267 |
+
</div>
|
| 268 |
+
<span className="text-[10px] font-black text-purple-400 opacity-0 group-hover/file:opacity-100 transition-all">DOWNLOAD</span>
|
| 269 |
</a>
|
| 270 |
))}
|
| 271 |
<a
|