Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 127 additions & 0 deletions government-tenders-finder/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# Project Title - Government Tender Finder for Singapore

**Live Link**: https://tender-scout-singapore.lovable.app


## What This Project Is -
This project is an AI-powered summer school discovery and comparison tool that automatically finds, scans, and extracts information from official summer school websites worldwide.
Instead of relying on outdated lists or manual searches, the app pulls live data directly from source websites and returns it in a clean, structured format.

## What This Project Is -

**AI-based Link Discovery**

The system first uses an AI layer to identify and curate relevant summer school websites based on the user’s query (region, subject, age group, duration, etc.).

**Automated Web Browsing with TinyFish**
The curated links are passed to the TinyFish Web Agent API, which launches multiple browser agents in parallel to:

1) Navigate dynamic pages

2) Handle real websites (no static scraping)

3) Extract structured details such as program name, location, dates, eligibility, fees, and deadlines

**Real-Time Streaming & Aggregation**
Progress updates are streamed via SSE, and extracted results are normalised into a unified JSON format for easy comparison.

## What to Expect

1) Live, up-to-date data pulled directly from official websites

2) Parallel web scanning for fast results

3) Real-time status updates during execution

4) Structured, comparable output (JSON)


**Demo Video** - https://drive.google.com/file/d/1GXZhJOjiVUP5XcGvTAvRGcYhTWoKXlsE/view?usp=sharing


## Code snippet -
```bash
const response = await fetch("https://mino.ai/v1/automation/run-sse", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": "sk-mino-YOUR_API_KEY",
},
body: JSON.stringify({
url: "https://www.gebiz.gov.sg",
goal: "Extract the latest open government tenders. Return JSON with tenderTitle, agency, tenderID, procurementCategory, submissionDeadline, eligibilityCriteria, estimatedValue, tenderStatus, and tenderLink.",
browser_profile: "lite",
}),
});

const reader = response.body!.getReader();
const decoder = new TextDecoder();

while (true) {
const { done, value } = await reader.read();
if (done) break;

const chunk = decoder.decode(value);
for (const line of chunk.split("\n")) {
if (line.startsWith("data: ")) {
const data = JSON.parse(line.slice(6));

// Live browser view
if (data.streamingUrl) {
console.log("Live view:", data.streamingUrl);
}

// Final structured output
if (data.type === "COMPLETE" && data.resultJson) {
console.log("Extracted tenders:", data.resultJson);
}
}
}
}
```


## Tech Stack
**Next.js (TypeScript)**

**Mino API**

**AI**

## Architecture Diagram
```mermaid
flowchart TB

%% =======================
%% UI LAYER
%% =======================
UI["USER INTERFACE<br/>(React + Tailwind + Lovable)"]

%% =======================
%% ORCHESTRATION
%% =======================
ORCH["Tender Search Orchestration Layer<br/>(Next.js API / Server Actions)"]

%% =======================
%% SERVICES
%% =======================
DB["SUPABASE<br/>(Cached Tenders & Metadata)"]
MINO["MINO API<br/>(Browser Automation)"]

%% =======================
%% DETAILS
%% =======================
DBD["• Cached tender listings<br/>• Deduplicated tenders<br/>• Historical records"]
MINOD["• Parallel web agents<br/>• Browse govt tender portals<br/>• Open tender pages<br/>• Extract structured fields<br/>• SSE streaming updates"]

%% =======================
%% CONNECTIONS
%% =======================
UI --> ORCH

ORCH --> DB
ORCH --> MINO

DB --> DBD
MINO --> MINOD
```
Binary file added government-tenders-finder/bun.lockb
Binary file not shown.
20 changes: 20 additions & 0 deletions government-tenders-finder/components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "src/index.css",
"baseColor": "slate",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
}
}
26 changes: 26 additions & 0 deletions government-tenders-finder/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import js from "@eslint/js";
import globals from "globals";
import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from "eslint-plugin-react-refresh";
import tseslint from "typescript-eslint";

export default tseslint.config(
{ ignores: ["dist"] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ["**/*.{ts,tsx}"],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
"react-hooks": reactHooks,
"react-refresh": reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
"react-refresh/only-export-components": ["warn", { allowConstantExport: true }],
"@typescript-eslint/no-unused-vars": "off",
},
},
);
26 changes: 26 additions & 0 deletions government-tenders-finder/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- TODO: Set the document title to the name of your application -->
<title>Lovable App</title>
<meta name="description" content="Lovable Generated Project" />
<meta name="author" content="Lovable" />

<!-- TODO: Update og:title to match your application name -->
<meta property="og:title" content="Lovable App" />
<meta property="og:description" content="Lovable Generated Project" />
<meta property="og:type" content="website" />
<meta property="og:image" content="https://lovable.dev/opengraph-image-p98pqg.png" />

<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@Lovable" />
<meta name="twitter:image" content="https://lovable.dev/opengraph-image-p98pqg.png" />
</head>

<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
Loading