Skip to main content
Glama
by Ritesh-sudo
App.tsx3.6 kB
import { useEffect, useMemo, useState } from 'react' import { Search, Loader2 } from 'lucide-react' import { JobCard } from './components/JobCard' import { SearchForm } from './components/SearchForm' import { JobPosting, JobSearchResponse } from './types/types' import { fetchJobs } from './utils/api' export default function App() { const [location, setLocation] = useState<string>('Remote') const [includeInternships, setIncludeInternships] = useState<boolean>(true) const [includeFullTime, setIncludeFullTime] = useState<boolean>(true) const [keywords, setKeywords] = useState<string>('') const [loading, setLoading] = useState<boolean>(false) const [error, setError] = useState<string>('') const [data, setData] = useState<JobSearchResponse | null>(null) const parsedKeywords = useMemo( () => keywords.split(',').map(k => k.trim()).filter(Boolean), [keywords] ) const onSearch = async () => { setLoading(true) setError('') try { const result = await fetchJobs({ location, includeInternships, includeFullTime, keywords: parsedKeywords }) setData(result) } catch (e: any) { setError(e?.message || 'Failed to fetch jobs') } finally { setLoading(false) } } useEffect(() => { onSearch() // eslint-disable-next-line react-hooks/exhaustive-deps }, []) const jobs: JobPosting[] = useMemo(() => { if (!data) return [] return data.results.flatMap(r => r.jobs) }, [data]) return ( <div className="min-h-screen bg-secondary-50 text-secondary-900"> <header className="sticky top-0 z-10 bg-white/80 backdrop-blur border-b border-secondary-200"> <div className="mx-auto max-w-6xl px-4 py-4 flex items-center gap-3"> <div className="p-2 rounded-lg bg-primary-100 text-primary-700"> <Search className="h-5 w-5" /> </div> <h1 className="text-xl font-semibold">AI/ML Job Search</h1> </div> </header> <main className="mx-auto max-w-6xl px-4 py-6"> <SearchForm location={location} setLocation={setLocation} includeInternships={includeInternships} setIncludeInternships={setIncludeInternships} includeFullTime={includeFullTime} setIncludeFullTime={setIncludeFullTime} keywords={keywords} setKeywords={setKeywords} onSearch={onSearch} loading={loading} /> {error && ( <div className="mt-4 rounded-md border border-red-200 bg-red-50 p-3 text-red-700"> {error} </div> )} <section className="mt-6"> <div className="mb-3 flex items-center justify-between"> <h2 className="text-lg font-semibold">Results</h2> {loading && ( <div className="flex items-center gap-2 text-secondary-600"> <Loader2 className="h-4 w-4 animate-spin" /> <span>Searching latest postings…</span> </div> )} </div> {jobs.length === 0 && !loading ? ( <div className="rounded-md border border-secondary-200 bg-white p-6 text-secondary-600"> No jobs found. Try changing filters or keywords. </div> ) : ( <div className="grid grid-cols-1 md:grid-cols-2 gap-4"> {jobs.map(job => ( <JobCard key={`${job.id}-${job.source}`} job={job} /> ))} </div> )} </section> </main> </div> ) }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Ritesh-sudo/MCPJobSearch'

If you have feedback or need assistance with the MCP directory API, please join our Discord server