import { useState, useEffect, useMemo } from 'react'; interface Job { id: string; company: string; position: string; location: string; remote: boolean; salary: string; description: string; applyUrl: string; source: string; sourceUrl: string; postedAt: string; tags: string[]; companyIcon: string; } interface JobsData { lastUpdated: string; totalJobs: number; sources: string[]; jobs: Job[]; } type LocationFilter = 'all' | 'remote' | 'onsite'; type SortOption = 'recent' | 'company' | 'salary'; const SOURCE_COLORS: Record = { HackerNews: 'bg-orange-500/15 text-orange-400 border-orange-500/20', RemoteOK: 'bg-blue-500/15 text-blue-400 border-blue-500/20', WeWorkRemotely: 'bg-emerald-500/15 text-emerald-400 border-emerald-500/20', Anthropic: 'bg-purple-500/15 text-purple-400 border-purple-500/20', }; function useGlobalAuth() { const [state, setState] = useState<{ isSignedIn: boolean; isLoaded: boolean }>({ isSignedIn: false, isLoaded: false, }); useEffect(() => { function check() { const clerk = (window as any).Clerk; if (clerk?.loaded) { const signedIn = !!clerk.user; setState((prev) => { if (prev.isLoaded && prev.isSignedIn === signedIn) return prev; return { isSignedIn: signedIn, isLoaded: true }; }); } } check(); const interval = setInterval(check, 500); const handleChange = () => check(); window.addEventListener('clerk:session', handleChange); return () => { clearInterval(interval); window.removeEventListener('clerk:session', handleChange); }; }, []); return state; } function timeAgo(dateStr: string): string { if (!dateStr) return ''; try { const date = new Date(dateStr); if (isNaN(date.getTime())) return ''; const now = new Date(); const days = Math.floor((now.getTime() - date.getTime()) / 86400000); if (days < 0 || isNaN(days)) return ''; if (days === 0) return 'Today'; if (days === 1) return '1d ago'; if (days < 30) return `${days}d ago`; if (days < 60) return '1mo ago'; return `${Math.floor(days / 30)}mo ago`; } catch { return ''; } } function safeUrl(url: string): string { return /^https?:\/\//i.test(url) ? url : '#'; } export default function JobsView() { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [locationFilter, setLocationFilter] = useState('all'); const [sourceFilter, setSourceFilter] = useState('all'); const [companyFilter, setCompanyFilter] = useState('all'); const [selectedTag, setSelectedTag] = useState('all'); const [sortBy, setSortBy] = useState('recent'); const [searchQuery, setSearchQuery] = useState(''); const { isSignedIn, isLoaded } = useGlobalAuth(); useEffect(() => { fetch('/claude-jobs.json') .then((r) => r.json()) .then((d) => { setData(d); setLoading(false); }) .catch(() => setLoading(false)); }, []); // Close dropdown when clicking outside useEffect(() => { const handleClickOutside = (e: MouseEvent) => { const target = e.target as HTMLElement; if (!target.closest('.company-dropdown')) { setIsCompanyDropdownOpen(false); } }; document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, []); const filteredJobs = useMemo(() => { if (!data) return []; let filtered = data.jobs.filter((job) => { if (locationFilter === 'remote' && !job.remote) return false; if (locationFilter === 'onsite' && job.remote) return false; if (sourceFilter !== 'all' && job.source !== sourceFilter) return false; if (companyFilter !== 'all' && job.company !== companyFilter) return false; if (selectedTag !== 'all' && !job.tags.includes(selectedTag)) return false; if (searchQuery) { const q = searchQuery.toLowerCase(); const text = `${job.company} ${job.position} ${job.location} ${job.tags.join(' ')}`.toLowerCase(); if (!text.includes(q)) return false; } return true; }); // Sort jobs filtered.sort((a, b) => { if (sortBy === 'recent') { return new Date(b.postedAt || 0).getTime() - new Date(a.postedAt || 0).getTime(); } else if (sortBy === 'company') { return a.company.localeCompare(b.company); } else if (sortBy === 'salary') { // Sort by salary (jobs with salary first) if (a.salary && !b.salary) return -1; if (!a.salary && b.salary) return 1; return 0; } return 0; }); return filtered; }, [data, locationFilter, sourceFilter, companyFilter, selectedTag, sortBy, searchQuery]); // Get unique companies and tags for filters const companies = useMemo(() => { if (!data) return []; const uniqueCompanies = Array.from(new Set(data.jobs.map(j => j.company))).sort(); return uniqueCompanies; }, [data]); // Get company logo for a given company name const getCompanyLogo = (companyName: string): string => { if (!data) return ''; const job = data.jobs.find(j => j.company === companyName); // If companyIcon exists in data, use it if (job?.companyIcon) return job.companyIcon; // Otherwise, try to fetch from Clearbit Logo API // Format: https://logo.clearbit.com/{domain} const domain = companyName.toLowerCase() .replace(/\s+/g, '') .replace(/[^a-z0-9]/g, ''); return `https://logo.clearbit.com/${domain}.com`; }; const allTags = useMemo(() => { if (!data) return []; const tagSet = new Set(); data.jobs.forEach(job => job.tags.forEach(tag => tagSet.add(tag))); return Array.from(tagSet).sort(); }, [data]); // Custom dropdown state const [isCompanyDropdownOpen, setIsCompanyDropdownOpen] = useState(false); function handleJobClick(e: React.MouseEvent, job: Job) { if (!isLoaded) { e.preventDefault(); return; } if (!isSignedIn) { e.preventDefault(); (window as any).Clerk?.openSignIn?.(); } // If signed in, let the navigate normally } if (loading) { return (
Loading jobs...
); } if (!data || data.jobs.length === 0) { return (
{/* Icon */}
{/* Message */}

No jobs available yet

We're currently curating exciting opportunities for AI developers. Check back soon for new job postings!

); } const remoteCount = data.jobs.filter((j) => j.remote).length; const onsiteCount = data.jobs.length - remoteCount; const showAuthGate = isLoaded && !isSignedIn; return (
{/* Header */}

Jobs Requiring Claude Code Claude Code

Companies actively hiring developers who use Claude Code in their workflow. Updated daily from HackerNews "Who is Hiring", RemoteOK, and more.

{/* Auth banner for non-signed-in users */} {showAuthGate && (

Sign in to view job details and apply to positions

)} {/* Stats cards */}
{[ { label: 'Total Jobs', value: String(data.totalJobs) }, { label: 'Remote', value: String(remoteCount) }, { label: 'On-site', value: String(onsiteCount) }, { label: 'Sources', value: String(data.sources.length) }, ].map((s) => (
{s.value}
{s.label}
))}
{/* Divider */}
{/* Filter bar - Enhanced */}
{/* Top row: Location, Source, Company, Sort */}
{/* Location filter */}
{(['all', 'remote', 'onsite'] as LocationFilter[]).map((loc) => ( ))}
{/* Source filter dropdown */}
{/* Company filter dropdown - Custom with logos */}
{/* Dropdown menu */} {isCompanyDropdownOpen && (
{companies.map((company) => ( ))}
)}
{/* Sort dropdown */}
{sortBy === 'recent' ? ( ) : sortBy === 'company' ? ( ) : ( )}
{/* Search */}
setSearchQuery(e.target.value)} className="bg-[var(--color-surface-2)] border border-[var(--color-border)] rounded-lg text-[12px] text-[var(--color-text-primary)] placeholder:text-[var(--color-text-tertiary)] pl-9 pr-3 py-1.5 w-56 outline-none focus:border-[var(--color-border-hover)] transition-colors" /> {searchQuery && ( )}
{/* Bottom row: Tags filter */} {allTags.length > 0 && (
Tags:
{allTags.slice(0, 12).map((tag) => ( ))} {allTags.length > 12 && ( +{allTags.length - 12} more )}
)} {/* Active filters indicator */} {(locationFilter !== 'all' || sourceFilter !== 'all' || companyFilter !== 'all' || selectedTag !== 'all' || searchQuery) && (
Active filters:
{locationFilter !== 'all' && ( {locationFilter} )} {sourceFilter !== 'all' && ( {sourceFilter} )} {companyFilter !== 'all' && ( {companyFilter} )} {selectedTag !== 'all' && ( {selectedTag} )} {searchQuery && ( "{searchQuery}" )}
)}
{/* Results count */}
Showing {filteredJobs.length} of {data.totalJobs} job{filteredJobs.length !== 1 ? 's' : ''} {filteredJobs.length === 0 && ( )}
{/* Job cards */} {filteredJobs.length === 0 ? (

No jobs found

Try adjusting your filters or search query to find more opportunities.

) : (
{filteredJobs.map((job) => ( handleJobClick(e, job)} className="block bg-[var(--color-card-bg)] border border-[var(--color-border)] rounded-lg p-4 hover:border-[var(--color-border-hover)] hover:bg-[var(--color-card-hover)] hover:shadow-lg transition-all group relative" >
{/* Company icon - Enhanced with internet fallback */}
{job.company} { const target = e.target as HTMLImageElement; target.style.display = 'none'; const fallback = target.nextElementSibling as HTMLElement; if (fallback) fallback.style.display = 'flex'; }} /> {job.company.charAt(0).toUpperCase()}
{/* Content */}

{job.position}

{job.salary && ( {job.salary} )}
{job.company} {job.location}
{job.remote && ( Remote )} {job.source} {job.postedAt && ( {timeAgo(job.postedAt)} )}
{job.description && (

{job.description}

)} {job.tags.length > 0 && (
{job.tags.slice(0, 6).map((tag) => ( { e.preventDefault(); e.stopPropagation(); setSelectedTag(tag); }} > {tag} ))} {job.tags.length > 6 && ( +{job.tags.length - 6} )}
)}
{/* Right side: apply arrow or lock icon */} {showAuthGate ? ( ) : ( )}
))}
)} {/* Footer */}

Last updated: {new Date(data.lastUpdated).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })} {' | '}Data sourced from HackerNews "Who is Hiring", RemoteOK, WeWorkRemotely, and Anthropic Careers

); }