// React via CDN, JSX compiled in-browser by Babel Standalone const sideNav = []; const sections = []; // Default catalog used for search/filter. Each item describes a course offering. const defaultCatalog = []; function inferDisplayDuration(name, level, field, durationYears) { if (durationYears && durationYears > 0) return durationYears; const n = (name || '').toLowerCase(); const lvl = (level || '').toLowerCase(); const fld = (field || '').toLowerCase(); if (n.includes('b.arch') || n.includes('architecture')) return 5.0; if (lvl === 'diploma') return 3.0; if (lvl === 'pg') return 2.0; if (fld === 'engineering' || n.includes('b.tech') || n.includes('b.e') || n.includes('engineering')) return 4.0; if (n.includes('b.pharm') || n.includes('pharmacy')) return 4.0; if (lvl === 'ug') return 3.0; return 3.0; } // Helper function to safely parse JSON responses (prevents HTML error pages from being parsed as JSON) async function safeJsonParse(response) { if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const text = await response.text(); // Check if response looks like HTML (common error page indicator) const trimmedText = text.trim(); if (trimmedText.startsWith('<') || trimmedText.startsWith('') || trimmedText.includes('')) { throw new Error(`Server returned HTML error page instead of JSON. Response: ${trimmedText.substring(0, 200)}`); } throw new Error(`Invalid JSON response: ${trimmedText.substring(0, 100)}`); } } // Helper function to try parsing JSON from any response (including error responses) for error message extraction async function tryParseJsonFromResponse(response) { const contentType = response.headers.get('content-type'); if (contentType && contentType.includes('application/json')) { const text = await response.text(); try { return JSON.parse(text); } catch (_) { return null; } } return null; } function useFinderOptions() { const [opts, setOpts] = React.useState({ categories: [], exams: [], rounds: [], inst_types: [], cities: [], programs: [], regions: [], years: [] }); React.useEffect(() => { let cancelled = false; (async () => { try { // Fetch options and catalog in parallel; merge programs to be robust const [rOpts, rCat] = await Promise.allSettled([ fetch('api/finder_options.php?t=' + Date.now()), // Add cache-busting parameter fetch('api/catalog.php') ]); let base = null; if (rOpts.status === 'fulfilled' && rOpts.value && rOpts.value.ok) { try { base = await safeJsonParse(rOpts.value); console.log('Finder options loaded:', base); // Debug log } catch (_) {} } if (!cancelled && base) { console.log('Cities loaded:', base.cities); // Debug cities specifically setOpts(base); } let names = []; if (rCat.status === 'fulfilled' && rCat.value && rCat.value.ok) { try { const cat = await safeJsonParse(rCat.value); // Filter to only engineering courses (field = 'Engineering' or name contains 'Engineering'/'ENGG') names = Array.from(new Set((Array.isArray(cat) ? cat : []) .filter(c => { if (!c || !c.name) return false; const nameUpper = String(c.name).toUpperCase(); const fieldUpper = String(c.field || '').toUpperCase(); return fieldUpper === 'ENGINEERING' || nameUpper.includes('ENGINEERING') || nameUpper.includes('ENGG'); }) .map(c => c && c.name) .filter(Boolean))).sort(); } catch (_) {} } if (!cancelled && names.length) { setOpts(prev => { const merged = Array.from(new Set([...(prev.programs || []), ...names])).sort(); return { ...prev, programs: merged }; }); } } catch (_) {} })(); return () => { cancelled = true; }; }, []); return opts; } function RankFinderPage() { const { categories, exams, rounds, inst_types, cities, programs, regions, years } = useFinderOptions(); const [rank, setRank] = React.useState(''); const [exam, setExam] = React.useState(''); const [category, setCategory] = React.useState('OPEN'); // Default to OPEN const [round, setRound] = React.useState(''); // Always empty - show all rounds const [instType, setInstType] = React.useState(''); const [city, setCity] = React.useState(''); const [program, setProgram] = React.useState(''); const [programChoices, setProgramChoices] = React.useState([]); const [selectedCourses, setSelectedCourses] = React.useState([]); const [selectedColleges, setSelectedColleges] = React.useState([]); const [collegeCutoffs, setCollegeCutoffs] = React.useState([]); const [isLoadingCutoffs, setIsLoadingCutoffs] = React.useState(false); const [allColleges, setAllColleges] = React.useState([]); // All available colleges // User Preferences & Saved Data const [userProfile, setUserProfile] = React.useState({ name: '', email: '', homeCity: '', preferences: { preferredCities: [], maxFee: 500000, minReputation: 0 } }); const [savedSearches, setSavedSearches] = React.useState([]); const [shortlistedColleges, setShortlistedColleges] = React.useState([]); const [showQuickActions, setShowQuickActions] = React.useState(false); // Comparison Tool States const [comparisonColleges, setComparisonColleges] = React.useState([]); const [showComparison, setShowComparison] = React.useState(false); // Analytics States const [admissionProbabilities, setAdmissionProbabilities] = React.useState({}); const [trendData, setTrendData] = React.useState([]); const [showAnalytics, setShowAnalytics] = React.useState(false); // Enhanced college cutoff display states const [cutoffViewMode, setCutoffViewMode] = React.useState('cards'); // 'cards', 'table', 'compact' const [cutoffPage, setCutoffPage] = React.useState(1); const [cutoffPageSize, setCutoffPageSize] = React.useState(6); // Programs per page const [cutoffSortBy, setCutoffSortBy] = React.useState('closing_rank'); // 'closing_rank', 'course_name', 'competition_level' const [cutoffSortOrder, setCutoffSortOrder] = React.useState('asc'); // 'asc', 'desc' const [cutoffSearchTerm, setCutoffSearchTerm] = React.useState(''); // const [region, setRegion] = React.useState(''); // REMOVED as per user request const [year, setYear] = React.useState('2025'); // Course-based college search states const [selectedCourseForColleges, setSelectedCourseForColleges] = React.useState(''); const [selectedCityForColleges, setSelectedCityForColleges] = React.useState(''); const [collegesByCourse, setCollegesByCourse] = React.useState([]); const [isLoadingCollegesByCourse, setIsLoadingCollegesByCourse] = React.useState(false); const [collegePage, setCollegePage] = React.useState(1); const [collegePageSize, setCollegePageSize] = React.useState(10); // Function to fetch colleges by course and city const fetchCollegesByCourse = React.useCallback(async (course, city = '') => { if (!course) { setCollegesByCourse([]); setLiveData({}); return; } setIsLoadingCollegesByCourse(true); try { let fetchUrl = `api/colleges_by_course.php?course=${encodeURIComponent(course)}&t=${Date.now()}`; if (city) { fetchUrl += `&city=${encodeURIComponent(city)}`; } console.log(`πŸ“‘ Fetching colleges for course: "${course}"${city ? `, city: "${city}"` : ''}`); console.log(`πŸ”— URL: ${fetchUrl}`); const response = await fetch(fetchUrl); console.log(`πŸ“¨ Response status: ${response.status} ${response.statusText}`); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await safeJsonParse(response); console.log(`πŸ“¦ API Response:`, data); console.log(`πŸ“Š Data structure:`, { hasColleges: 'colleges' in data, collegesType: Array.isArray(data.colleges) ? 'array' : typeof data.colleges, collegesLength: Array.isArray(data.colleges) ? data.colleges.length : 'N/A', dataKeys: Object.keys(data) }); const colleges = data.colleges || []; console.log(`βœ… Extracted ${colleges.length} colleges`); if (colleges.length === 0) { console.warn(`⚠️ No colleges returned for course: "${course}"${city ? `, city: "${city}"` : ''}`); console.warn(` API response:`, data); } setCollegesByCourse(colleges); // Automatically fetch live data for ALL colleges with websites if (colleges.length > 0) { console.log(`πŸš€ Starting automatic fetch for ${colleges.length} colleges`); let collegesWithWebsite = 0; const fetchPromises = []; // Fetch data for all colleges with websites colleges.forEach((college, index) => { // Check if college has a valid website (more lenient validation) const website = college.website ? String(college.website).trim() : ''; const hasWebsite = website && website !== '#' && website !== '' && !website.startsWith('#') && website.length > 3 && (website.includes('.') || website.startsWith('http')); if (hasWebsite) { collegesWithWebsite++; // Create fetch function const fetchLiveData = async () => { try { setFetchingLiveData(prev => ({...prev, [college.id]: true})); console.log(`[${index + 1}/${colleges.length}] 🌐 Fetching live data for: ${college.name} (ID: ${college.id}, Website: ${website})`); const fetchUrl = `api/fetch_college_data_live.php?id=${college.id}&t=${Date.now()}`; console.log(`πŸ“‘ Making fetch request to: ${fetchUrl}`); const liveResponse = await fetch(fetchUrl); console.log(`πŸ“¨ Response status: ${liveResponse.status} ${liveResponse.statusText}`); const liveDataResult = await safeJsonParse(liveResponse); console.log(`πŸ“₯ Live data result for ${college.name}:`, liveDataResult); if (liveDataResult && liveDataResult.fetched_data) { const fetched = liveDataResult.fetched_data; console.log(`βœ… Fetched data for ${college.name}:`, fetched); // Set data if we got anything if (fetched.hostel_facilities || fetched.fees || fetched.contact_info) { setLiveData(prev => { const newData = { ...prev, [college.id]: { hostel: fetched.hostel_facilities || null, fees: fetched.fees || null, contact: fetched.contact_info || null } }; console.log(`βœ“ Updated liveData for ${college.name}:`, newData[college.id]); return newData; }); } else { console.log(`⚠️ No data found for ${college.name}`); } } else { console.log(`⚠️ No fetched_data in response for ${college.name}:`, liveDataResult); } } catch (error) { console.error(`❌ Error fetching live data for college ${college.id} (${college.name}):`, error); console.error(` Error details:`, error.message, error.stack); } finally { setFetchingLiveData(prev => ({...prev, [college.id]: false})); } }; // For first 5 colleges, fetch immediately; others with delay if (index < 5) { fetchPromises.push(fetchLiveData()); } else { setTimeout(() => { fetchPromises.push(fetchLiveData()); }, (index - 4) * 300); // 300ms delay after first 5 } } else { console.log(`⏭️ Skipping ${college.name} - no valid website (${website || 'N/A'})`); } }); console.log(`πŸ“Š Total colleges with websites: ${collegesWithWebsite} out of ${colleges.length}`); console.log(`πŸ”„ Started fetching for first ${Math.min(5, collegesWithWebsite)} colleges immediately`); // Log sample of colleges being processed if (colleges.length > 0) { console.log(`πŸ“‹ Sample colleges:`, colleges.slice(0, 3).map(c => ({ id: c.id, name: c.name, website: c.website }))); } } } catch (error) { console.error('Error fetching colleges by course:', error); setCollegesByCourse([]); } finally { setIsLoadingCollegesByCourse(false); } }, []); const [expandedCollegeId, setExpandedCollegeId] = React.useState(null); const [liveData, setLiveData] = React.useState({}); // Store live fetched data: {collegeId: {hostel, fees, contact}} const [fetchingLiveData, setFetchingLiveData] = React.useState({}); // Track which colleges are being fetched const [rows, setRows] = React.useState([]); const [metadata, setMetadata] = React.useState(null); const [isSearching, setIsSearching] = React.useState(false); const [error, setError] = React.useState(''); const [hasAttemptedSearch, setHasAttemptedSearch] = React.useState(false); const lastAbortRef = React.useRef(null); // Notifications state const [notifications, setNotifications] = React.useState([]); const [showAllNotifications, setShowAllNotifications] = React.useState(false); // Approved Boards state const [approvedBoards, setApprovedBoards] = React.useState([]); const [boardStates, setBoardStates] = React.useState([]); const [boardFilter, setBoardFilter] = React.useState(''); const [boardSearch, setBoardSearch] = React.useState(''); const [showApprovedBoards, setShowApprovedBoards] = React.useState(false); const [isLoadingBoards, setIsLoadingBoards] = React.useState(false); // Fetch notifications on component mount React.useEffect(() => { fetch('api/admissions.php') .then(safeJsonParse) .then(data => { const notifs = data?.ug_engineering?.notifications || []; // Sort notifications by date (newest first), then by id (descending) to ensure latest is first const sortedNotifs = Array.isArray(notifs) ? notifs.sort((a, b) => { const dateA = new Date(a.date || 0); const dateB = new Date(b.date || 0); if (dateB.getTime() !== dateA.getTime()) { return dateB.getTime() - dateA.getTime(); // Newest first } return (b.id || 0) - (a.id || 0); // Higher ID first if dates are equal }) : []; setNotifications(sortedNotifs); }) .catch(() => setNotifications([])); }, []); // Fetch approved boards on component mount React.useEffect(() => { setIsLoadingBoards(true); fetch('api/approved_boards.php') .then(safeJsonParse) .then(data => { if (data.success) { setApprovedBoards(Array.isArray(data.boards) ? data.boards : []); setBoardStates(Array.isArray(data.states) ? data.states : []); } }) .catch(() => { setApprovedBoards([]); setBoardStates([]); }) .finally(() => setIsLoadingBoards(false)); }, []); // Filter approved boards based on state filter and search const filteredBoards = React.useMemo(() => { let filtered = [...approvedBoards]; if (boardFilter) { filtered = filtered.filter(b => b.state_ut && b.state_ut.toLowerCase().includes(boardFilter.toLowerCase()) ); } if (boardSearch) { filtered = filtered.filter(b => (b.board_name && b.board_name.toLowerCase().includes(boardSearch.toLowerCase())) || (b.state_ut && b.state_ut.toLowerCase().includes(boardSearch.toLowerCase())) ); } return filtered; }, [approvedBoards, boardFilter, boardSearch]); // Category normalization function - standardize category names for display const normalizeCategoryDisplay = React.useCallback((cat) => { if (!cat) return cat; const catUpper = String(cat).toUpperCase().trim(); // Map common variations to standard names const categoryMap = { 'GEN': 'OPEN', 'GENERAL': 'OPEN', 'OPEN': 'OPEN', 'SC': 'SC', 'ST': 'ST', 'SEBC': 'SEBC', 'OBC': 'SEBC', 'EWS': 'EWS', 'ESM': 'ESM', 'TFWS': 'TFWS', 'PH': 'PH', 'PWD': 'PH' }; return categoryMap[catUpper] || cat; }, []); // Helper function to format rank values - show "-" instead of 0 or empty const formatRank = React.useCallback((rank, formattedRank) => { // Check formatted rank first if (formattedRank && formattedRank !== '0' && formattedRank !== 0 && formattedRank !== '') { return formattedRank; } // Check unformatted rank if (rank && rank !== '0' && rank !== 0 && rank !== '') { const rankNum = typeof rank === 'string' ? parseInt(rank.replace(/[,\s]/g, ''), 10) : rank; if (!isNaN(rankNum) && rankNum > 0) { return rank; } } // Return dash if no valid rank return 'β€”'; }, []); // Normalize and prepare parameters for API const normalizedParams = React.useMemo(() => { const trim = (s) => (s || '').toString().trim(); const up = (s) => trim(s).toUpperCase(); let cat = up(category); // Preserve GEN for backend SP; mapping to OPEN is done server-side for LAR let ex = up(exam); if (ex !== 'GUJCET' && ex !== 'JEE') ex = ex || 'GUJCET'; const params = new URLSearchParams(); const rnk = parseInt(trim(rank), 10); if (!isNaN(rnk) && rnk > 0) params.set('rank', String(rnk)); // Always send all parameters, even if empty, for proper filtering params.set('exam', ex || ''); params.set('category', cat || ''); params.set('round', trim(round) || ''); params.set('inst_type', trim(instType) || ''); params.set('city', trim(city) || ''); // Handle multiple course selection if (selectedCourses.length > 0) { params.set('courses', selectedCourses.join(',')); } else { params.set('program', trim(program) || ''); } // params.set('region', trim(region) || ''); // REMOVED as per user request const yr = parseInt(String(year || '').trim(), 10); if (!isNaN(yr) && yr > 0) params.set('year', String(yr)); // Debug: Log what parameters are being sent console.log('Search parameters:', Object.fromEntries(params)); console.log('Category value:', category); console.log('Normalized category:', cat); return params; }, [rank, exam, category, round, instType, city, program, selectedCourses, year]); // Pagination state const [page, setPage] = React.useState(1); const [pageSize, setPageSize] = React.useState(10); // Do not auto-default filters; keep them empty unless user selects // Do not auto-select Program; user must choose explicitly // Default other dropdowns to first available option when options load // Do not auto-default filters; keep them empty (ALL) unless user selects // Do not auto-select Round; user must choose explicitly // Do not auto-default Institute Type; keep it empty (ALL) unless user selects // Region auto-defaulting removed as per user request // Build Program choices by merging options API and catalog course names // Filter to show only engineering courses React.useEffect(() => { let cancelled = false; (async () => { try { const base = Array.isArray(programs) ? programs.slice() : []; let merged = base; try { const rc = await fetch('api/catalog.php'); if (rc.ok) { const cat = await safeJsonParse(rc); // Filter to only engineering courses (field = 'Engineering' or name contains 'Engineering'/'ENGG') const names = Array.from(new Set((Array.isArray(cat) ? cat : []) .filter(c => { if (!c || !c.name) return false; const nameUpper = String(c.name).toUpperCase(); const fieldUpper = String(c.field || '').toUpperCase(); return fieldUpper === 'ENGINEERING' || nameUpper.includes('ENGINEERING') || nameUpper.includes('ENGG'); }) .map(c => c && c.name) .filter(Boolean))); merged = Array.from(new Set([...(base || []), ...names])).sort(); } } catch (_) {} if (!cancelled) { setProgramChoices(merged); } } catch (_) {} })(); return () => { cancelled = true; }; }, [programs]); // Reset to first page when filters change React.useEffect(() => { setPage(1); }, [rank, exam, category, round, instType, city, program, pageSize]); // Region page reset removed as per user request React.useEffect(() => { setPage(1); }, [year]); // Reset cutoff page when data changes React.useEffect(() => { setCutoffPage(1); }, [collegeCutoffs, cutoffSearchTerm, cutoffSortBy, cutoffSortOrder]); // Load all colleges on component mount React.useEffect(() => { let cancelled = false; (async () => { try { const response = await fetch('api/colleges.php?t=' + Date.now()); if (response.ok) { const data = await safeJsonParse(response); if (!cancelled && data && Array.isArray(data.all_colleges)) { setAllColleges(data.all_colleges); } } } catch (error) { console.error('Failed to load colleges:', error); } })(); return () => { cancelled = true; }; }, []); // Helper functions for college cutoff processing const processCollegeCutoffs = React.useMemo(() => { if (!collegeCutoffs || collegeCutoffs.length === 0) return { processedColleges: [], totalPrograms: 0 }; let allPrograms = []; // Flatten all programs from all colleges collegeCutoffs.forEach(college => { college.programs.forEach(program => { allPrograms.push({ ...program, college_name: college.college_name, college_index: collegeCutoffs.indexOf(college) }); }); }); // Apply search filter if (cutoffSearchTerm) { allPrograms = allPrograms.filter(program => program.course_name.toLowerCase().includes(cutoffSearchTerm.toLowerCase()) || program.college_name.toLowerCase().includes(cutoffSearchTerm.toLowerCase()) || program.category?.toLowerCase().includes(cutoffSearchTerm.toLowerCase()) || program.round?.toLowerCase().includes(cutoffSearchTerm.toLowerCase()) ); } // Apply sorting allPrograms.sort((a, b) => { let aVal, bVal; switch (cutoffSortBy) { case 'closing_rank': aVal = parseInt(a.closing_rank) || 999999; bVal = parseInt(b.closing_rank) || 999999; break; case 'course_name': aVal = a.course_name.toLowerCase(); bVal = b.course_name.toLowerCase(); break; case 'competition_level': const levelOrder = { 'Very High': 1, 'High': 2, 'Medium': 3, 'Low': 4, 'Very Low': 5 }; aVal = levelOrder[a.competition_level] || 6; bVal = levelOrder[b.competition_level] || 6; break; default: aVal = a[cutoffSortBy]; bVal = b[cutoffSortBy]; } if (cutoffSortOrder === 'desc') { return aVal > bVal ? -1 : aVal < bVal ? 1 : 0; } else { return aVal < bVal ? -1 : aVal > bVal ? 1 : 0; } }); // Apply pagination const startIndex = (cutoffPage - 1) * cutoffPageSize; const endIndex = startIndex + cutoffPageSize; const paginatedPrograms = allPrograms.slice(startIndex, endIndex); // Group paginated programs back by college const processedColleges = []; const collegeMap = new Map(); paginatedPrograms.forEach(program => { if (!collegeMap.has(program.college_name)) { collegeMap.set(program.college_name, { college_name: program.college_name, programs: [] }); } collegeMap.get(program.college_name).programs.push(program); }); return { processedColleges: Array.from(collegeMap.values()), totalPrograms: allPrograms.length, totalPages: Math.ceil(allPrograms.length / cutoffPageSize) }; }, [collegeCutoffs, cutoffSearchTerm, cutoffSortBy, cutoffSortOrder, cutoffPage, cutoffPageSize]); // Compute year choices with a safe fallback to current/last year const yearChoices = React.useMemo(() => { if (Array.isArray(years) && years.length > 0) return years; const now = new Date().getFullYear(); return [now, now - 1]; }, [years]); // Keep year empty by default to allow backend to choose best dataset // Instant search on input (debounced, abortable) React.useEffect(() => { const id = setTimeout(() => { const rnk = parseInt(String(rank || '').trim(), 10); const hasValidRank = !isNaN(rnk) && rnk > 0; const selectedYear = parseInt(String(year || '').trim(), 10); const isYear2024 = !isNaN(selectedYear) && selectedYear === 2024; // Rank is mandatory UNLESS year 2024 is selected (show all data like home page) if (!hasValidRank && !isYear2024) { console.log('No search: Rank is required (or select year 2024)'); // Don't set error here - only show error if user has attempted to search setRows([]); setMetadata(null); return; } // Mark that a search attempt is being made setHasAttemptedSearch(true); // Round selection removed - always show all rounds console.log('Executing search with params:', Object.fromEntries(normalizedParams)); (async () => { setIsSearching(true); setError(''); // Clear any previous errors // Abort previous in-flight request if (lastAbortRef.current) { try { lastAbortRef.current.abort(); } catch (_) {} } const ac = new AbortController(); lastAbortRef.current = ac; try { const r = await fetch('api/rank_finder.php?' + normalizedParams.toString(), { signal: ac.signal }); if (!r.ok) { let msg = 'Search failed'; try { const j = await tryParseJsonFromResponse(r); if (j && j.error) msg = j.error; } catch (_) {} throw new Error(msg); } const data = await safeJsonParse(r); console.log('API Response:', data); console.log('Results count:', Array.isArray(data) ? data.length : (data.results ? data.results.length : 0)); // Debug: Log first result to check open_rank const firstResult = Array.isArray(data) ? data[0] : (data.results ? data.results[0] : null); if (firstResult) { console.log('First result - College:', firstResult.college); console.log('First result - open_rank:', firstResult.open_rank, 'open_rank_formatted:', firstResult.open_rank_formatted); console.log('First result - closing_rank:', firstResult.closing_rank, 'closing_rank_formatted:', firstResult.closing_rank_formatted); } // Filter out invalid records const filterValidRecords = (rows) => { if (!Array.isArray(rows)) return []; const userRank = parseInt(String(rank || '').trim(), 10); const hasValidRank = !isNaN(userRank) && userRank > 0; const isYear2024View = parseInt(String(year || '').trim(), 10) === 2024 && !hasValidRank; return rows.filter(r => { // Check if closing rank is null/empty (placeholder row from API when no data available) const closingRankStr = String(r.closing_rank || r.closing_rank_formatted || '').replace(/[,\s]/g, ''); const closingRank = parseInt(closingRankStr, 10); const hasClosingRank = !isNaN(closingRank) && closingRank > 0; // Allow records with null/empty closing_rank (these are placeholder rows when no data is available) // Only filter out records with closing_rank = 0 (invalid data, not placeholder) if (r.closing_rank === 0 || (r.closing_rank_formatted === '0' && hasClosingRank === false)) { return false; // Exclude records with closing_rank explicitly set to 0 } // If user entered a rank and record has a valid closing rank, check eligibility (closing_rank >= user_rank) // Skip this check for placeholder rows (null/empty closing_rank) or if viewing year 2024 without rank if (hasValidRank && !isYear2024View && hasClosingRank) { if (userRank > closingRank) { return false; // User rank is worse than closing rank, not eligible } } // Filter out records with invalid college names const collegeName = String(r.college || r.college_name || '').trim(); if (!collegeName || collegeName === 'β€”' || collegeName === '') { return false; // Exclude empty college names } // Filter out placeholder/invalid college names const invalidPatterns = [ /^first year/i, /^engineering\s+\d{4}/i, /^year\s+\d{4}/i, /^\d{4}-\d{2,4}$/i, // Just years like "2025-26" /^program/i, /^course/i ]; if (invalidPatterns.some(pattern => pattern.test(collegeName))) { return false; // Exclude invalid college name patterns } return true; // Valid record (including placeholder rows with null closing_rank) }); }; // Handle both old format (array) and new format (object with results) let validRows = []; if (Array.isArray(data)) { validRows = filterValidRecords(data); setRows(validRows); } else if (data && Array.isArray(data.results)) { validRows = filterValidRecords(data.results); setRows(validRows); // Store metadata if available if (data.metadata) { setMetadata(data.metadata); } } else { setRows([]); } console.log('Valid results after filtering:', validRows.length); } catch (e) { if (e.name !== 'AbortError') { setError(e.message || 'Search failed'); setRows([]); } } finally { setIsSearching(false); } })(); }, 400); return () => clearTimeout(id); }, [normalizedParams, rank, exam, category, round, instType, city, program, year]); const search = async () => { // Mark that user has attempted to search setHasAttemptedSearch(true); const rnk = parseInt(String(rank || '').trim(), 10); const hasValidRank = !isNaN(rnk) && rnk > 0; const selectedYear = parseInt(String(year || '').trim(), 10); const isYear2024 = !isNaN(selectedYear) && selectedYear === 2024; // Rank is mandatory UNLESS year 2024 is selected (show all data like home page) if (!hasValidRank && !isYear2024) { setError('Please enter your rank to search'); setRows([]); setMetadata(null); return; } // Round selection removed - always show all rounds console.log('Manual search executing with params:', Object.fromEntries(normalizedParams)); setIsSearching(true); setError(''); // Clear any previous errors try { // Abort previous in-flight request if (lastAbortRef.current) { try { lastAbortRef.current.abort(); } catch (_) {} } const ac = new AbortController(); lastAbortRef.current = ac; const r = await fetch('api/rank_finder.php?' + normalizedParams.toString(), { signal: ac.signal }); if (!r.ok) { let msg = 'Search failed'; try { const j = await tryParseJsonFromResponse(r); if (j && j.error) msg = j.error; } catch (_) {} throw new Error(msg); } const data = await safeJsonParse(r); console.log('Manual search API Response:', data); console.log('Manual search Results count:', Array.isArray(data) ? data.length : (data.results ? data.results.length : 0)); // Frontend deduplication: Remove duplicates based on college_id and course_id // Also filter out invalid records const filterValidRecords = (rows) => { if (!Array.isArray(rows)) return []; const userRank = parseInt(String(rank || '').trim(), 10); const hasValidRank = !isNaN(userRank) && userRank > 0; const isYear2024View = parseInt(String(year || '').trim(), 10) === 2024 && !hasValidRank; return rows.filter(r => { // Check if closing rank is null/empty (placeholder row from API when no data available) const closingRankStr = String(r.closing_rank || r.closing_rank_formatted || '').replace(/[,\s]/g, ''); const closingRank = parseInt(closingRankStr, 10); const hasClosingRank = !isNaN(closingRank) && closingRank > 0; // Allow records with null/empty closing_rank (these are placeholder rows when no data is available) // Only filter out records with closing_rank = 0 (invalid data, not placeholder) if (r.closing_rank === 0 || (r.closing_rank_formatted === '0' && hasClosingRank === false)) { return false; // Exclude records with closing_rank explicitly set to 0 } // If user entered a rank and record has a valid closing rank, check eligibility (closing_rank >= user_rank) // Skip this check for placeholder rows (null/empty closing_rank) or if viewing year 2024 without rank if (hasValidRank && !isYear2024View && hasClosingRank) { if (userRank > closingRank) { return false; // User rank is worse than closing rank, not eligible } } // Filter out records with invalid college names const collegeName = String(r.college || r.college_name || '').trim(); if (!collegeName || collegeName === 'β€”' || collegeName === '') { return false; // Exclude empty college names } // Filter out placeholder/invalid college names const invalidPatterns = [ /^first year/i, /^engineering\s+\d{4}/i, /^year\s+\d{4}/i, /^\d{4}-\d{2,4}$/i, // Just years like "2025-26" /^program/i, /^course/i ]; if (invalidPatterns.some(pattern => pattern.test(collegeName))) { return false; // Exclude invalid college name patterns } return true; // Valid record (including placeholder rows with null closing_rank) }); }; let processedData = []; if (Array.isArray(data)) { processedData = filterValidRecords(data); } else if (data && Array.isArray(data.results)) { processedData = filterValidRecords(data.results); } // Deduplication logic - ensure only one record per college-course combination const uniqueRows = []; const seen = new Set(); processedData.forEach(row => { const key = `${row.college_id}-${row.course_id}`; if (!seen.has(key)) { seen.add(key); uniqueRows.push(row); } }); // Handle both old format (array) and new format (object with results) if (Array.isArray(data)) { setRows(uniqueRows); setMetadata({ total: uniqueRows.length, original_total: data.length }); } else if (data && Array.isArray(data.results)) { setRows(uniqueRows); setMetadata({ ...data.metadata, total: uniqueRows.length, original_total: data.results.length }); } else { setRows([]); setMetadata(null); } console.log('Valid results after filtering:', uniqueRows.length); // Debug logging console.log(`Search completed: ${uniqueRows.length} unique results (${processedData.length} total before deduplication)`); } catch (e) { if (e.name !== 'AbortError') { setError(e.message || 'Search failed.'); setRows([]); } } finally { setIsSearching(false); } }; const fetchCollegeCutoffs = async () => { if (selectedColleges.length === 0) return; console.log('Fetching cutoffs for colleges:', selectedColleges); setIsLoadingCutoffs(true); try { const params = new URLSearchParams(); params.set('colleges', selectedColleges.join('|')); // Use | as delimiter instead of comma params.set('year', year || '2025'); console.log('API URL:', `api/college_cutoffs.php?${params}`); const response = await fetch(`api/college_cutoffs.php?${params}`); if (!response.ok) throw new Error('Failed to fetch college cutoffs'); const data = await safeJsonParse(response); console.log('Received data:', data); setCollegeCutoffs(data); } catch (e) { console.error('Error fetching college cutoffs:', e); setError('Failed to load college cutoffs'); } finally { setIsLoadingCutoffs(false); } }; const total = rows.length; const totalPages = Math.max(1, Math.ceil(total / pageSize)); const startIdx = (page - 1) * pageSize; const endIdx = startIdx + pageSize; const visibleRows = rows.slice(startIdx, endIdx); return ( <>

🎯 Rank-based College Finder

Enter your details to discover colleges where you can get admission based on your rank

setRank(e.target.value)} placeholder="e.g., 12345" /> Common merit rank issued by ACPC (same for all categories)
Category refers to seat reservation, not a separate rank
{selectedCourses.length > 0 && (
{selectedCourses.map((course, index) => ( {course} ))}
)}
{/* Helper text note */}
Note: ACPC merit rank is common for all categories. Category selection is used only to evaluate eligibility for reserved seats.
{/* Results Table - Only show if rank is entered OR year 2024 is selected */} {((rank && parseInt(String(rank || '').trim(), 10) > 0) || (parseInt(String(year || '').trim(), 10) === 2024)) && (() => { const isYear2024View = parseInt(String(year || '').trim(), 10) === 2024 && (!rank || parseInt(String(rank || '').trim(), 10) <= 0); // Determine which round is being used (from metadata or first result) const roundUsed = metadata?.round_used || (rows.length > 0 ? rows[0]?.round : null); const roundLabel = roundUsed ? (roundUsed.toLowerCase().includes('vacant') ? 'Vacant Round (Final Counselling Round)' : roundUsed.toLowerCase().includes('round 3') || roundUsed.toLowerCase().includes('round-3') ? 'Round 3 (Last Available Counselling Round)' : roundUsed.toLowerCase().includes('round 2') || roundUsed.toLowerCase().includes('round-2') ? 'Round 2 (Last Available Counselling Round)' : roundUsed.toLowerCase().includes('round 1') || roundUsed.toLowerCase().includes('round-1') ? 'Round 1 (Last Available Counselling Round)' : roundUsed) : 'Last Available Counselling Round'; return (
{/* Results banner */} {rows.length > 0 && (
Results based on {roundLabel} of the selected year
Category indicates seat type, not a separate merit rank. Closing rank shown is for selected category in the final counselling round.
)}
{!isYear2024View && } {!isYear2024View && } {!isYear2024View && } {!isYear2024View && } {visibleRows.length === 0 ? ( ) : visibleRows.map((r,i)=> ( { e.currentTarget.style.background = 'rgba(37, 99, 235, 0.05)'; e.currentTarget.style.transform = 'translateY(-2px)'; e.currentTarget.style.boxShadow = '0 4px 15px rgba(0,0,0,0.1)'; }} onMouseLeave={(e) => { e.currentTarget.style.background = i % 2 === 0 ? '#ffffff' : '#f8fafc'; e.currentTarget.style.transform = 'translateY(0)'; e.currentTarget.style.boxShadow = 'none'; }}> {!isYear2024View && } {!isYear2024View && } {!isYear2024View && } {!isYear2024View && ( )} ))}
CollegeCityTypeProgram CategoryStatusClosing RankCompetition
No results found. Try adjusting your search criteria.
{r.college} {!isYear2024View && r.college_code &&
{r.college_code}
}
{r.city || 'β€”'} {r.inst_type || 'β€”'}
{r.course}
{!isYear2024View && r.duration_formatted &&
{r.duration_formatted}
}
{normalizeCategoryDisplay(r.category)} {r.status || 'Eligible'} {formatRank(r.closing_rank, r.closing_rank_formatted)} {(() => { // Calculate competition level based on difference between user rank and closing rank const userRank = parseInt(String(rank || '').trim(), 10); const closingRankStr = String(r.closing_rank || r.closing_rank_formatted || '').replace(/[,\s]/g, ''); const closingRank = parseInt(closingRankStr, 10); let competitionLevel = 'β€”'; let bgColor = 'rgba(107, 114, 128, 0.1)'; let textColor = '#6b7280'; // Only calculate if we have both user rank and closing rank if (!isNaN(userRank) && userRank > 0 && !isNaN(closingRank) && closingRank > 0) { // Calculate the difference between closing rank and user rank const rankDifference = closingRank - userRank; // Calculate percentage difference: (difference / user_rank) * 100 const percentageDifference = (rankDifference / userRank) * 100; // If difference is small (user rank is close to closing rank) β†’ High competition // If difference is large (user rank is much better than closing rank) β†’ Medium competition // Threshold: If difference is less than 50% of user rank, it's High competition if (percentageDifference <= 50) { // Small difference - user rank is close to closing rank β†’ High competition competitionLevel = 'High'; bgColor = 'rgba(217, 119, 6, 0.1)'; textColor = '#d97706'; } else { // Large difference - user rank is much better than closing rank β†’ Medium competition competitionLevel = 'Medium'; bgColor = 'rgba(37, 99, 235, 0.1)'; textColor = '#2563eb'; } } else if (!isNaN(closingRank) && closingRank > 0) { // If no user rank provided, default to Medium competition competitionLevel = 'Medium'; bgColor = 'rgba(37, 99, 235, 0.1)'; textColor = '#2563eb'; } return ( {competitionLevel} ); })()}
); })()} {((rank && parseInt(String(rank || '').trim(), 10) > 0) || (parseInt(String(year || '').trim(), 10) === 2024)) && rows.length > 0 && (
{total.toLocaleString()} results found {metadata && ( (Comprehensive search with all data sources) )}
)} {error &&
{error}
} {/* Eligibility Status Message - Only show if rank is entered (not for year 2024 view without rank) */} {rows.length > 0 && rank && parseInt(String(rank || '').trim(), 10) > 0 && (() => { const userRank = parseInt(String(rank || '').trim(), 10); // Check if there are any rows with valid closing ranks (not placeholder rows) const rowsWithValidClosingRank = rows.filter(r => { const closingRankStr = String(r.closing_rank || r.closing_rank_formatted || '').replace(/[,\s]/g, ''); const closingRank = parseInt(closingRankStr, 10); return !isNaN(closingRank) && closingRank > 0; }); // Only show eligibility message if there are rows with valid closing ranks // If all rows are placeholder rows (no valid closing_rank), don't show the message if (rowsWithValidClosingRank.length === 0) { return null; } const eligibleColleges = rowsWithValidClosingRank.filter(r => { // Handle both formatted and unformatted closing ranks const closingRankStr = String(r.closing_rank || r.closing_rank_formatted || '').replace(/[,\s]/g, ''); const closingRank = parseInt(closingRankStr, 10); // User is eligible if their rank is less than or equal to closing rank (lower rank number is better) return !isNaN(closingRank) && closingRank > 0 && userRank <= closingRank; }); const isEligible = eligibleColleges.length > 0; const totalColleges = rowsWithValidClosingRank.length; const eligibilityPercentage = totalColleges > 0 ? Math.round((eligibleColleges.length / totalColleges) * 100) : 0; return (
{isEligible ? 'βœ…' : '❌'}

{isEligible ? `Congratulations! You are Eligible for Admission` : `Sorry, You are Not Eligible for Admission`}

{isEligible ? ( <> Great news! Based on your rank {userRank.toLocaleString()}, you are eligible for admission in {eligibleColleges.length} out of {totalColleges} colleges/programs ({eligibilityPercentage}%). {eligibleColleges.length > 0 && (
You have a {eligibilityPercentage}% chance of getting admission based on the current search results.
)} ) : ( <> Unfortunately, based on your rank {userRank.toLocaleString()}, you are not eligible for admission in any of the {totalColleges} colleges/programs shown in the current search results.
Suggestions:
  • Try adjusting your search filters (category, city, program type)
  • Consider colleges in different cities or with different fee ranges
  • Check if there are other rounds or admission processes available
  • Review the closing ranks in the results to see what rank range is needed
)}
{isEligible && eligibleColleges.length > 0 && (
{eligibleColleges.length}
Eligible Options
)}
); })()} {/* Results Section - Only show if rank is entered */} {((rank && parseInt(String(rank || '').trim(), 10) > 0) || (parseInt(String(year || '').trim(), 10) === 2024)) && (

Results

{/* Analytics Section */} {showAnalytics && rows.length > 0 && (

πŸ“Š Admission Analytics

{(() => { const userRank = parseInt(String(rank || '').trim(), 10); if (isNaN(userRank) || userRank <= 0) return 'β€”'; const eligible = rows.filter(r => { const closingRankStr = String(r.closing_rank || r.closing_rank_formatted || '').replace(/[,\s]/g, ''); const closingRank = parseInt(closingRankStr, 10); return !isNaN(closingRank) && closingRank > 0 && userRank <= closingRank; }); return rows.length > 0 ? Math.round((eligible.length / rows.length) * 100) : 0; })()}%
Admission Probability
{(() => { const userRank = parseInt(String(rank || '').trim(), 10); if (isNaN(userRank) || userRank <= 0) return 0; return rows.filter(r => { const closingRankStr = String(r.closing_rank || r.closing_rank_formatted || '').replace(/[,\s]/g, ''); const closingRank = parseInt(closingRankStr, 10); return !isNaN(closingRank) && closingRank > 0 && userRank <= closingRank; }).length; })()}
Eligible Colleges
β‚Ή{Math.round(rows.reduce((sum, r) => sum + (r.fee_inr || 0), 0) / rows.length).toLocaleString()}
Avg Fee
{(() => { const validRanks = rows.filter(r => { const rank = r.closing_rank || r.closing_rank_formatted; if (!rank || rank === 0 || rank === '0') return false; const rankNum = typeof rank === 'string' ? parseInt(rank.replace(/[,\s]/g, ''), 10) : rank; return !isNaN(rankNum) && rankNum > 0; }); if (validRanks.length === 0) return 'β€”'; const sum = validRanks.reduce((s, r) => { const rank = r.closing_rank || r.closing_rank_formatted; const rankNum = typeof rank === 'string' ? parseInt(String(rank).replace(/[,\s]/g, ''), 10) : rank; return s + (rankNum || 0); }, 0); return Math.round(sum / validRanks.length).toLocaleString(); })()}
Avg Closing Rank
)} {/* Comparison Tool Section */} {rows.length > 0 && (

πŸ” College Comparison Tool

Select colleges to compare:
{rows.slice(0, 5).map((row, index) => ( ))}
{comparisonColleges.length > 0 && ( )}
{/* Comparison Table */} {showComparison && comparisonColleges.length > 0 && (
{comparisonColleges.map((college, index) => ( ))}
College Course Fee Closing Rank Competition Chance
{college.college} {college.course} β‚Ή{college.fee_inr?.toLocaleString() || 'N/A'} {college.closing_rank && college.closing_rank > 0 ? college.closing_rank.toLocaleString() : 'β€”'} {(() => { // Calculate competition based on difference between user rank and closing rank const userRank = parseInt(String(rank || '').trim(), 10); const closingRank = college.closing_rank ? parseInt(String(college.closing_rank).replace(/[,\s]/g, ''), 10) : 0; let competitionLevel = 'β€”'; let bgColor = '#e3f2fd'; let textColor = '#1565c0'; if (!isNaN(userRank) && userRank > 0 && !isNaN(closingRank) && closingRank > 0) { const rankDifference = closingRank - userRank; const percentageDifference = (rankDifference / userRank) * 100; if (percentageDifference <= 50) { competitionLevel = 'High'; bgColor = '#fff3e0'; textColor = '#ef6c00'; } else { competitionLevel = 'Medium'; bgColor = '#e8f5e8'; textColor = '#2e7d32'; } } else if (!isNaN(closingRank) && closingRank > 0) { competitionLevel = 'High'; bgColor = '#e8f5e8'; textColor = '#2e7d32'; } return ( {competitionLevel} ); })()} { const userRank = parseInt(String(rank || '').trim(), 10); if (isNaN(userRank) || userRank <= 0) return '#ffebee'; const closingRankStr = String(college.closing_rank || '').replace(/[,\s]/g, ''); const closingRank = parseInt(closingRankStr, 10); const isEligible = !isNaN(closingRank) && closingRank > 0 && userRank <= closingRank; return isEligible ? '#e8f5e8' : '#ffebee'; })(), color: (() => { const userRank = parseInt(String(rank || '').trim(), 10); if (isNaN(userRank) || userRank <= 0) return '#c62828'; const closingRankStr = String(college.closing_rank || '').replace(/[,\s]/g, ''); const closingRank = parseInt(closingRankStr, 10); const isEligible = !isNaN(closingRank) && closingRank > 0 && userRank <= closingRank; return isEligible ? '#2e7d32' : '#c62828'; })() }}> {(() => { const userRank = parseInt(String(rank || '').trim(), 10); if (isNaN(userRank) || userRank <= 0) return '❌ Not Eligible'; const closingRankStr = String(college.closing_rank || '').replace(/[,\s]/g, ''); const closingRank = parseInt(closingRankStr, 10); const isEligible = !isNaN(closingRank) && closingRank > 0 && userRank <= closingRank; return isEligible ? 'βœ… Eligible' : '❌ Not Eligible'; })()}
)}
)} {/* Download Options */} {rows.length > 0 && (
)} {/* Pagination Controls */}
Page {page} of {totalPages}
{/* Metadata Display */} {metadata && (
Search Results: {metadata.total || metadata.total_results} unique college-course combinations {metadata.original_total && metadata.original_total > (metadata.total || metadata.total_results) && ( (deduplicated from {metadata.original_total} total records) )}
Generated: {metadata.generated_at}
{/* Analytics Display */} {metadata.analytics && (

Search Analytics

Admission Status
{metadata.analytics.high_chance_count} High Chance, {metadata.analytics.eligible_count} Eligible
Avg. Probability
70 ? '#10b981' : metadata.analytics.avg_admission_probability > 40 ? '#f59e0b' : '#ef4444'}}> {metadata.analytics.avg_admission_probability}%
Search Quality
{metadata.analytics.search_effectiveness}
{metadata.analytics.top_colleges && metadata.analytics.top_colleges.length > 0 && (
Top Colleges
{metadata.analytics.top_colleges.slice(0, 3).join(', ')} {metadata.analytics.top_colleges.length > 3 && '...'}
)}
)}
)}
)} {/* Show message if rank is not entered AND user has attempted to search */} {hasAttemptedSearch && (!rank || parseInt(String(rank || '').trim(), 10) <= 0) && !(parseInt(String(year || '').trim(), 10) === 2024) && (
πŸ”

Rank is Required

Please enter your rank in the search form above to view admission results.

)} {/* View All Notifications Modal */} {showAllNotifications && (
setShowAllNotifications(false)}>
e.stopPropagation()}>

All Notifications ({notifications.length})

{notifications.map((n, i) => (
{ e.preventDefault(); e.stopPropagation(); setShowAllNotifications(false); // Use the same logic as above to open notification try { if (typeof openNotificationBrochure === 'function') { openNotificationBrochure(n, i); } else { const modal = document.getElementById('brochureModal'); const modalTitle = document.getElementById('brochureModalTitle'); if (modal && modalTitle) { modalTitle.innerHTML = `${n.title || 'Notification'}`; if (typeof loadBrochureContent === 'function') { loadBrochureContent(n, i); } else { const modalContent = document.getElementById('brochureContent'); const downloadLink = document.getElementById('brochureDownloadLink'); if (modalContent) { if (n.brochure_file) { modalContent.innerHTML = `
`; if (downloadLink) { downloadLink.href = n.brochure_file; downloadLink.style.display = 'inline-block'; } } else { modalContent.innerHTML = `

${n.title || 'Notification'}

${n.date || 'N/A'}

This notification contains important information regarding the admission process.

${n.url && n.url !== '#' ? `

External Link: View Full Notification

` : '' } ${n.brochure_title ? `

Brochure: ${n.brochure_title}

` : ''}
`; if (downloadLink) { downloadLink.style.display = 'none'; } } } } modal.style.display = 'flex'; } else if (n.url && n.url !== '#') { window.open(n.url, '_blank'); } } } catch (err) { console.error('Error opening notification:', err); if (n.url && n.url !== '#') { window.open(n.url, '_blank'); } } }} style={{ background: '#f9fafb', borderRadius: '12px', padding: '16px', cursor: 'pointer', transition: 'all 0.3s ease', border: '1px solid #e5e7eb', borderLeft: '4px solid #667eea' }} onMouseEnter={(e) => { e.currentTarget.style.transform = 'translateY(-4px)'; e.currentTarget.style.boxShadow = '0 8px 24px rgba(0,0,0,0.15)'; e.currentTarget.style.background = 'white'; }} onMouseLeave={(e) => { e.currentTarget.style.transform = 'translateY(0)'; e.currentTarget.style.boxShadow = 'none'; e.currentTarget.style.background = '#f9fafb'; }} >
{n.date || 'N/A'}
{n.title || 'Notification'}
{n.url && n.url !== '#' && (
Has external link
)}
))}
)} {/* College-wise Cutoff Section */}

πŸ›οΈ College-wise Cutoff Analysis

{/* Helpful message */} {rows.length === 0 && allColleges.length > 0 && (
πŸ’‘ Tip: Perform a search first to see colleges from your results, or select from all available colleges below.
)} {/* Selection counter */}
= 5 ? '#dc3545' : '#6c757d' }}> Selected: {selectedColleges.length}/5 colleges {selectedColleges.length >= 5 && ( ⚠️ Maximum reached )}
{(() => { // Helper function to normalize college name for deduplication - more aggressive const normalizeCollegeName = (name) => { if (!name) return ''; let normalized = name.trim() .toUpperCase() .replace(/\s+/g, ' ') .trim(); // Normalize common abbreviations and variations normalized = normalized.replace(/\bENGG\.?\b/g, 'ENGINEERING'); normalized = normalized.replace(/\bTECH\.?\b/g, 'TECHNOLOGY'); normalized = normalized.replace(/\bINST\.?\b/g, 'INSTITUTE'); normalized = normalized.replace(/\bCOLL\.?\b/g, 'COLLEGE'); normalized = normalized.replace(/\bUNIV\.?\b/g, 'UNIVERSITY'); normalized = normalized.replace(/\b&/g, 'AND'); normalized = normalized.replace(/\./g, ''); // Remove all dots normalized = normalized.replace(/\s+/g, ' ').trim(); return normalized; }; // Helper function to get base college name (first meaningful part) const getBaseCollegeName = (name) => { if (!name) return ''; const normalized = normalizeCollegeName(name); // Get the first part before comma, dash, or parentheses (main college name) const base = normalized.split(/[,\-\(]/)[0].trim(); // Remove common suffixes return base.replace(/\s+(UNIVERSITY|INSTITUTE|COLLEGE|TECHNOLOGY|TECH|ENGINEERING|ENGG)[,\s]*$/i, '').trim() || base; }; // Helper function to get display name (main college name only, no branch/campus details) const getDisplayName = (name) => { if (!name) return ''; // Remove everything after comma, dash, parentheses, or common branch indicators let display = name.split(/[,\(]/)[0].trim(); // Remove common branch/faculty patterns (more aggressive) display = display.replace(/\s*-\s*(FACULTY|BRANCH|CAMPUS|DEPARTMENT|OF|FOR).*$/i, ''); display = display.replace(/\s*\(.*$/i, ''); // Remove contact info patterns (Contact No:, Email:, Website:) display = display.replace(/\s*(Contact No|Email|Website|Contact|Phone|Tel):.*$/i, ''); // Remove address patterns (common address keywords) display = display.replace(/\s+(At|Near|Beside|Opposite|Behind|Road|Street|Avenue|Lane).*$/i, ''); // Clean up extra spaces and trim display = display.replace(/\s+/g, ' ').trim(); return display; }; // Process search results first (priority), then allColleges const seenNormalizedNames = new Set(); // Track normalized display names (no spaces) we've seen const uniqueCollegesFromRows = []; // Colleges from search results const uniqueAllColleges = []; // Colleges from allColleges // First, process search results (they have priority) if (rows.length > 0) { rows.forEach(r => { if (!r.college || selectedColleges.includes(r.college)) return; const fullName = r.college; const displayName = getDisplayName(fullName); if (!displayName || !displayName.trim()) return; // Normalize for comparison const normalizedDisplay = normalizeCollegeName(displayName); const normalizedNoSpaces = normalizedDisplay.replace(/\s+/g, ''); // Add to seen set and to search results list if (!seenNormalizedNames.has(normalizedNoSpaces)) { seenNormalizedNames.add(normalizedNoSpaces); uniqueCollegesFromRows.push({ source: 'search', college: r.college, college_id: r.college_id, fullName: fullName, displayName: displayName }); } }); } // Then, process allColleges (exclude those already in search results by normalized name) if (allColleges.length > 0) { allColleges.forEach(college => { if (!college.name || selectedColleges.includes(college.name)) return; // Skip if exact match exists in search results if (rows.length > 0 && rows.some(r => r.college === college.name)) return; const fullName = college.name; const displayName = getDisplayName(fullName); if (!displayName || !displayName.trim()) return; // Normalize for comparison const normalizedDisplay = normalizeCollegeName(displayName); const normalizedNoSpaces = normalizedDisplay.replace(/\s+/g, ''); // Only add if not already seen (from search results) if (!seenNormalizedNames.has(normalizedNoSpaces)) { seenNormalizedNames.add(normalizedNoSpaces); uniqueAllColleges.push({ source: 'all', college: college.name, college_id: college.id, fullName: fullName, displayName: displayName }); } }); } return ( ); })()} {selectedColleges.length > 0 && (
{selectedColleges.map((college, index) => ( {college} ))}
)}
{/* Debug Info */} {selectedColleges.length > 0 && (
Debug Info:
Selected Colleges: {selectedColleges.join(', ')}
College Cutoffs Count: {collegeCutoffs.length}
Loading: {isLoadingCutoffs ? 'Yes' : 'No'}
)} {/* College Cutoffs Results - Separate Sections for Each College */} {collegeCutoffs.length > 0 && (
{/* Enhanced Header with Controls */}

πŸŽ“ College-wise Cutoff Analysis

{processCollegeCutoffs.totalPrograms} programs from {collegeCutoffs.length} college{collegeCutoffs.length > 1 ? 's' : ''}

{/* View Mode Toggle */}
{['cards', 'table', 'compact'].map(mode => ( ))}
{/* Search and Sort Controls */}
{/* Search */}
setCutoffSearchTerm(e.target.value)} style={{ width: '100%', padding: '8px 12px', borderRadius: '8px', border: '1px solid rgba(255,255,255,0.2)', background: 'rgba(255,255,255,0.1)', color: 'white', fontSize: '14px' }} />
{/* Sort Controls */}
{/* Page Size */}
Per page:
{/* Pagination Info */} {processCollegeCutoffs.totalPages > 1 && (
Showing {((cutoffPage - 1) * cutoffPageSize) + 1} to {Math.min(cutoffPage * cutoffPageSize, processCollegeCutoffs.totalPrograms)} of {processCollegeCutoffs.totalPrograms} programs
{Array.from({length: Math.min(5, processCollegeCutoffs.totalPages)}, (_, i) => { const pageNum = Math.max(1, Math.min(processCollegeCutoffs.totalPages - 4, cutoffPage - 2)) + i; return ( ); })}
)} {/* Dynamic Content Based on View Mode */} {cutoffViewMode === 'cards' && (
{processCollegeCutoffs.processedColleges.map((college, collegeIndex) => (
{/* College Header */}

πŸ›οΈ {college.college_name}

{college.programs.length} program{college.programs.length > 1 ? 's' : ''} available

{/* Programs Grid */}
{college.programs.map((program, programIndex) => (
{ e.currentTarget.style.background = 'rgba(255,255,255,0.1)'; e.currentTarget.style.transform = 'translateY(-2px)'; e.currentTarget.style.boxShadow = '0 4px 15px rgba(0,0,0,0.1)'; }} onMouseLeave={(e) => { e.currentTarget.style.background = 'rgba(255,255,255,0.05)'; e.currentTarget.style.transform = 'translateY(0)'; e.currentTarget.style.boxShadow = 'none'; }}>
πŸ“š {program.course_name}
Open Rank
{formatRank(program.open_rank, program.open_rank_formatted)}
Closing Rank
{formatRank(program.closing_rank, program.closing_rank_formatted)}
Category
{normalizeCategoryDisplay(program.category) || 'β€”'}
Round
{program.round || 'β€”'}
{/* Competition Level Badge */}
{(() => { // Calculate competition based on difference between user rank and closing rank const userRank = parseInt(String(rank || '').trim(), 10); const closingRank = program.closing_rank ? parseInt(String(program.closing_rank).replace(/[,\s]/g, ''), 10) : 0; let competitionLevel = 'β€”'; let bgColor = 'rgba(107, 114, 128, 0.1)'; let textColor = '#6b7280'; if (!isNaN(userRank) && userRank > 0 && !isNaN(closingRank) && closingRank > 0) { const rankDifference = closingRank - userRank; const percentageDifference = (rankDifference / userRank) * 100; if (percentageDifference <= 50) { competitionLevel = 'High'; bgColor = 'rgba(217, 119, 6, 0.1)'; textColor = '#d97706'; } else { competitionLevel = 'Medium'; bgColor = 'rgba(37, 99, 235, 0.1)'; textColor = '#2563eb'; } } else if (!isNaN(closingRank) && closingRank > 0) { competitionLevel = 'Medium'; bgColor = 'rgba(37, 99, 235, 0.1)'; textColor = '#2563eb'; } return ( Competition: {competitionLevel} ); })()}
))}
))}
)} {/* Table View */} {cutoffViewMode === 'table' && (

πŸ“Š All Programs Comparison Table

{processCollegeCutoffs.processedColleges.map((college, collegeIndex) => college.programs.map((program, programIndex) => ( { e.currentTarget.style.background = 'rgba(255,255,255,0.05)'; }} onMouseLeave={(e) => { e.currentTarget.style.background = 'transparent'; }}> )) )}
College Program Open Rank Closing Rank Category Round Competition
{college.college_name} {program.course_name} {formatRank(program.open_rank, program.open_rank_formatted)} {formatRank(program.closing_rank, program.closing_rank_formatted)} {normalizeCategoryDisplay(program.category) || 'β€”'} {program.round || 'β€”'} {(() => { // Calculate competition based on difference between user rank and closing rank const userRank = parseInt(String(rank || '').trim(), 10); const closingRank = program.closing_rank ? parseInt(String(program.closing_rank).replace(/[,\s]/g, ''), 10) : 0; let competitionLevel = 'β€”'; let bgColor = 'rgba(107, 114, 128, 0.1)'; let textColor = '#6b7280'; if (!isNaN(userRank) && userRank > 0 && !isNaN(closingRank) && closingRank > 0) { const rankDifference = closingRank - userRank; const percentageDifference = (rankDifference / userRank) * 100; if (percentageDifference <= 50) { competitionLevel = 'High'; bgColor = 'rgba(217, 119, 6, 0.1)'; textColor = '#d97706'; } else { competitionLevel = 'Medium'; bgColor = 'rgba(37, 99, 235, 0.1)'; textColor = '#2563eb'; } } else if (!isNaN(closingRank) && closingRank > 0) { competitionLevel = 'Medium'; bgColor = 'rgba(37, 99, 235, 0.1)'; textColor = '#2563eb'; } return ( {competitionLevel} ); })()}
)} {/* Compact View */} {cutoffViewMode === 'compact' && (

πŸ“‹ Compact Program List

{processCollegeCutoffs.processedColleges.map((college, collegeIndex) => college.programs.map((program, programIndex) => (
{ e.currentTarget.style.background = 'rgba(255,255,255,0.1)'; e.currentTarget.style.transform = 'translateX(5px)'; }} onMouseLeave={(e) => { e.currentTarget.style.background = 'rgba(255,255,255,0.05)'; e.currentTarget.style.transform = 'translateX(0)'; }}>
{program.course_name}
{college.college_name}
Open
{formatRank(program.open_rank, program.open_rank_formatted)}
Closing
{formatRank(program.closing_rank, program.closing_rank_formatted)}
Category
{normalizeCategoryDisplay(program.category) || 'β€”'}
Round
{program.round || 'β€”'}
{(() => { // Calculate competition based on difference between user rank and closing rank const userRank = parseInt(String(rank || '').trim(), 10); const closingRank = program.closing_rank ? parseInt(String(program.closing_rank).replace(/[,\s]/g, ''), 10) : 0; let competitionLevel = 'β€”'; let bgColor = 'rgba(107, 114, 128, 0.1)'; let textColor = '#6b7280'; if (!isNaN(userRank) && userRank > 0 && !isNaN(closingRank) && closingRank > 0) { const rankDifference = closingRank - userRank; const percentageDifference = (rankDifference / userRank) * 100; if (percentageDifference <= 50) { competitionLevel = 'High'; bgColor = 'rgba(217, 119, 6, 0.1)'; textColor = '#d97706'; } else { competitionLevel = 'Medium'; bgColor = 'rgba(37, 99, 235, 0.1)'; textColor = '#2563eb'; } } else if (!isNaN(closingRank) && closingRank > 0) { competitionLevel = 'Medium'; bgColor = 'rgba(37, 99, 235, 0.1)'; textColor = '#2563eb'; } return ( {competitionLevel} ); })()}
)) )}
)}
)} {/* πŸŽ“ Course-based College Search Section */}
{/* Header Section */}

πŸŽ“ Find Colleges by Course

Select a course to view all colleges offering that program with complete details

{/* Course and City Selection */}
{/* Loading State */} {isLoadingCollegesByCourse && (

Loading colleges...

)} {/* Colleges Display - Comprehensive Table Layout */} {!isLoadingCollegesByCourse && selectedCourseForColleges && collegesByCourse.length > 0 && (
{collegesByCourse.length}
Colleges offering {selectedCourseForColleges}
Complete information displayed in table format
{/* Main Colleges Table */}
{(() => { // Pagination logic const startIndex = (collegePage - 1) * collegePageSize; const endIndex = startIndex + collegePageSize; const paginatedColleges = collegesByCourse.slice(startIndex, endIndex); const totalPages = Math.ceil(collegesByCourse.length / collegePageSize); return paginatedColleges.map((college, idx) => { try { // Validate college object if (!college || typeof college !== 'object') { throw new Error('Invalid college object'); } // Parse college name and address to extract all information const fullName = college.name || ''; const fullAddress = college.address || ''; const combinedText = (fullName + ' ' + fullAddress).trim(); // Extract contact number from name or address const contactMatch = combinedText.match(/Contact\s+No[:\s]*([\d\s\-]+)/i); const extractedContact = contactMatch ? contactMatch[1].trim() : null; // Extract email from name or address const emailMatch = combinedText.match(/Email[:\s]*([^\s,]+@[^\s,]+)/i); const extractedEmail = emailMatch ? emailMatch[1].trim() : null; // Extract website from name or address - improved regex to capture valid URLs only let extractedWebsite = null; // Try multiple patterns to find website // Pattern 1: "Website: www.domain.com" or "Website: domain.com" const websiteMatch1 = combinedText.match(/(?:Website|W)[:\s]+(?:www\.)?([a-zA-Z0-9][a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/i); if (websiteMatch1 && websiteMatch1[1]) { let website = websiteMatch1[1].trim(); // Validate it's a proper domain (has TLD) if (/^[a-zA-Z0-9][a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(website)) { if (!website.startsWith('www.') && !website.startsWith('http')) { extractedWebsite = 'www.' + website; } else { extractedWebsite = website; } } } // Pattern 2: "www.domain.com" or "http://domain.com" or "https://domain.com" if (!extractedWebsite) { const websiteMatch2 = combinedText.match(/(?:https?:\/\/|www\.)([a-zA-Z0-9][a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/i); if (websiteMatch2 && websiteMatch2[1]) { let website = websiteMatch2[1].trim(); if (/^[a-zA-Z0-9][a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(website)) { if (combinedText.match(/https?:\/\//i)) { extractedWebsite = combinedText.match(/(https?:\/\/[^\s,]+)/i)?.[1] || null; } else { extractedWebsite = 'www.' + website; } } } } // Clean extracted website - remove any trailing punctuation or invalid characters if (extractedWebsite) { extractedWebsite = extractedWebsite .replace(/[.,;:!?]+$/, '') // Remove trailing punctuation .replace(/\s+/g, '') // Remove spaces .trim(); // Final validation - must be a valid domain format if (!/^(https?:\/\/)?(www\.)?[a-zA-Z0-9][a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(extractedWebsite)) { extractedWebsite = null; } } // Extract district from address or name let extractedDistrict = null; // Try to extract district from address field first if (fullAddress) { // Look for district patterns in address (e.g., "Ahmedabad District", "Kheda Dist") const districtMatch = fullAddress.match(/\b([A-Z][a-z]+(?:\s+[A-Z][a-z]+)*)\s*(?:District|Dist\.?)/i); if (districtMatch) { extractedDistrict = districtMatch[1].trim(); } } // If not found, try from name if (!extractedDistrict && fullName) { const districtMatch = fullName.match(/\b([A-Z][a-z]+(?:\s+[A-Z][a-z]+)*)\s*(?:District|Dist\.?)/i); if (districtMatch) { extractedDistrict = districtMatch[1].trim(); } } // Also check if district is in the database field if (!extractedDistrict && college.district) { extractedDistrict = college.district; } // Extract address parts (At, Post, Near patterns) - but exclude district, email, website, contact let extractedAddress = ''; // Improved regex that stops at Contact/Email/Website patterns const atMatch = combinedText.match(/At\s+([^,]+?)(?:\s*,\s*|Contact|Email|Website|$)/i); const postMatch = combinedText.match(/Post\s+([^,]+?)(?:\s*,\s*|Contact|Email|Website|$)/i); const nearMatch = combinedText.match(/Near\s+([^,]+?)(?:\s*,\s*|Contact|Email|Website|$)/i); // Helper function to clean address parts - removes contact/email/website const cleanAddressPart = (part) => { if (!part) return ''; return part .replace(/\s*Contact\s+No[:\s]*[\d\s\-()]+/gi, '') .replace(/\s*Phone[:\s]*[\d\s\-()]+/gi, '') .replace(/\s*Tel[:\s]*[\d\s\-()]+/gi, '') .replace(/\s*Mob[:\s]*[\d\s\-()]+/gi, '') .replace(/\s*Email[:\s]*[^\s,]+@[^\s,]+\s*/gi, '') .replace(/\s*[^\s,]+@[^\s,]+\s*/gi, '') .replace(/\s*Website[:\s]*[^\s,]+/gi, '') .replace(/\s*W\s+www\.[^\s,]+\s*/gi, '') .replace(/\s*www\.[^\s,]+\s*/gi, '') .replace(/\s*https?:\/\/[^\s,]+\s*/gi, '') .replace(/\s+/g, ' ') .trim(); }; if (atMatch && atMatch[1]) { const cleaned = cleanAddressPart(atMatch[1].trim()); if (cleaned && !cleaned.match(/Contact|Email|Website|@|www\.|http/i)) { extractedAddress += (extractedAddress ? ', ' : '') + 'At ' + cleaned; } } if (postMatch && postMatch[1]) { const cleaned = cleanAddressPart(postMatch[1].trim()); if (cleaned && !cleaned.match(/Contact|Email|Website|@|www\.|http/i)) { extractedAddress += (extractedAddress ? ', ' : '') + 'Post ' + cleaned; } } if (nearMatch && nearMatch[1]) { const cleaned = cleanAddressPart(nearMatch[1].trim()); if (cleaned && !cleaned.match(/Contact|Email|Website|@|www\.|http/i)) { extractedAddress += (extractedAddress ? ', ' : '') + 'Near ' + cleaned; } } // Clean college name - remove all extracted information let cleanCollegeName = fullName .replace(/\s*Contact\s+No[:\s]*[\d\s\-]+/gi, '') .replace(/\s*Email[:\s]*[^\s]+@[^\s]+\s*/gi, '') .replace(/\s*Website[:\s]*[^\s]+\s*/gi, '') .replace(/\s*W\s+www\.[^\s]+\s*/gi, '') .replace(/\s*At\s+[^,]+/gi, '') .replace(/\s*Post\s+[^,]+/gi, '') .replace(/\s*Near\s+[^,]+/gi, '') .replace(/\s*,\s*,/g, ',') // Remove double commas .replace(/\s*,\s*$/g, '') // Remove trailing comma .replace(/^\s*,\s*/g, '') // Remove leading comma .trim(); // If name still contains commas, take only the first part (usually the actual name) if (cleanCollegeName.includes(',')) { const parts = cleanCollegeName.split(',').map(p => p.trim()).filter(p => p); cleanCollegeName = parts[0]; // If we don't have address yet, use remaining parts (but exclude district, email, website) if (!extractedAddress && parts.length > 1) { const addressParts = parts.slice(1).filter(p => { const pLower = p.toLowerCase(); return !pLower.includes('@') && !pLower.includes('www.') && !pLower.includes('website') && !pLower.includes('district') && !pLower.includes('dist'); }); if (addressParts.length > 0) { extractedAddress = addressParts.join(', '); } } } // Clean address - remove ALL non-address information (email, website, district, contact, city, state, pincode) // This function will be used multiple times to ensure thorough cleaning const cleanAddressString = (addr) => { if (!addr) return ''; let cleaned = addr; // Remove district if it was extracted if (extractedDistrict) { cleaned = cleaned .replace(new RegExp(`\\b${extractedDistrict.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*(?:District|Dist\\.?)`, 'gi'), '') .replace(/\s*District[:\s]*[^,]+/gi, '') .replace(/\s*Dist[:\s]*[^,]+/gi, ''); } // Remove all non-address information - multiple passes for thoroughness for (let i = 0; i < 3; i++) { // Multiple passes to catch nested patterns cleaned = cleaned // Remove contact information (various formats) .replace(/\s*Contact\s+No[:\s]*[\d\s\-()]+/gi, '') .replace(/\s*Contact[:\s]*[\d\s\-()]+/gi, '') .replace(/\s*Phone[:\s]*[\d\s\-()]+/gi, '') .replace(/\s*Tel[:\s]*[\d\s\-()]+/gi, '') .replace(/\s*Mob[:\s]*[\d\s\-()]+/gi, '') .replace(/\s*Mobile[:\s]*[\d\s\-()]+/gi, '') // Remove email (various formats) .replace(/\s*Email[:\s]*[^\s,]+@[^\s,]+\s*/gi, '') .replace(/\s*E-mail[:\s]*[^\s,]+@[^\s,]+\s*/gi, '') .replace(/\s*[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\s*/gi, '') // Standalone email pattern // Remove website (various formats) .replace(/\s*Website[:\s]*[^\s,]+/gi, '') .replace(/\s*W\s+www\.[^\s,]+\s*/gi, '') .replace(/\s*www\.[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\s*/gi, '') // www.domain.com pattern .replace(/\s*https?:\/\/[^\s,]+\s*/gi, '') .replace(/\s*http:\/\/[^\s,]+\s*/gi, '') .replace(/\s*https:\/\/[^\s,]+\s*/gi, '') // Remove district .replace(/\s*District[:\s]*[^,]+/gi, '') .replace(/\s*Dist[:\s]*[^,]+/gi, '') // Remove city, state, pincode if they appear as separate items .replace(/\s*City[:\s]*[^,]+/gi, '') .replace(/\s*State[:\s]*[^,]+/gi, '') .replace(/\s*Pincode[:\s]*[\d\s]+/gi, '') .replace(/\s*Pin[:\s]*[\d\s]+/gi, '') .replace(/\s*PIN[:\s]*[\d\s]+/gi, ''); } // Clean up multiple commas and spaces cleaned = cleaned .replace(/\s*,\s*,/g, ',') .replace(/\s*,\s*,\s*/g, ',') .replace(/\s*,\s*$/g, '') .replace(/^\s*,\s*/g, '') .replace(/\s+/g, ' ') // Multiple spaces to single space .trim(); return cleaned; }; let cleanAddress = cleanAddressString(fullAddress || ''); // Use extracted values or fall back to database values const displayContact = extractedContact || college.contact_no || null; const displayEmail = extractedEmail || college.email || null; // Validate and clean database website before using let dbWebsite = null; if (college.website && college.website !== '#' && college.website.trim() !== '') { const cleanedDbWebsite = college.website.trim(); // Validate it's a proper URL/domain if (/^(https?:\/\/)?(www\.)?[a-zA-Z0-9][a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(cleanedDbWebsite.replace(/^https?:\/\//, '').replace(/^www\./, ''))) { dbWebsite = cleanedDbWebsite; } } // Extract website from email if website is not available let websiteFromEmail = null; if (!extractedWebsite && !dbWebsite && extractedEmail) { // Extract domain from email (part after @) const emailDomainMatch = extractedEmail.match(/@([a-zA-Z0-9][a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/i); if (emailDomainMatch && emailDomainMatch[1]) { const domain = emailDomainMatch[1].trim(); // Validate it's a proper domain if (/^[a-zA-Z0-9][a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(domain)) { websiteFromEmail = domain; } } } // Also try to extract from database email if available if (!extractedWebsite && !dbWebsite && !websiteFromEmail && college.email) { const emailDomainMatch = college.email.match(/@([a-zA-Z0-9][a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/i); if (emailDomainMatch && emailDomainMatch[1]) { const domain = emailDomainMatch[1].trim(); if (/^[a-zA-Z0-9][a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(domain)) { websiteFromEmail = domain; } } } // Prefer extracted website, fallback to database website, then website from email const displayWebsite = extractedWebsite || dbWebsite || websiteFromEmail; // Combine extracted address parts (At, Post, Near) with cleaned address // Clean extracted address parts first using the same cleaning function const cleanedExtractedAddress = extractedAddress ? cleanAddressString(extractedAddress) : ''; // Combine cleaned parts let finalAddress = ''; if (cleanedExtractedAddress) { finalAddress = cleanedExtractedAddress; } if (cleanAddress) { if (finalAddress) { // Avoid duplicates - check if cleanAddress is already in finalAddress if (!finalAddress.toLowerCase().includes(cleanAddress.toLowerCase()) && !cleanAddress.toLowerCase().includes(finalAddress.toLowerCase())) { finalAddress += ', ' + cleanAddress; } } else { finalAddress = cleanAddress; } } // If we still don't have address, try using the raw address but clean it thoroughly if (!finalAddress && fullAddress) { finalAddress = cleanAddressString(fullAddress); } // Final cleanup pass - remove any remaining contact/email/website that might have slipped through if (finalAddress) { // Apply cleaning function multiple times finalAddress = cleanAddressString(finalAddress); finalAddress = cleanAddressString(finalAddress); // Second pass // Split by comma and filter out any parts containing contact/email/website const addressParts = finalAddress.split(',').map(p => p.trim()).filter(p => { if (!p) return false; const pLower = p.toLowerCase(); // Reject if contains email pattern if (/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/.test(p)) return false; // Reject if contains website pattern if (/www\.|https?:\/\//.test(p)) return false; // Reject if contains contact keywords if (/contact|phone|tel|mob|mobile|email|website|e-mail/i.test(p)) return false; // Reject if it's just a number (likely a phone number) if (/^\d{10,}$/.test(p.replace(/[\s\-()]/g, ''))) return false; // Reject if it looks like a domain name if (/^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(p) && !p.includes(' ')) return false; return true; }); // Rejoin valid address parts finalAddress = addressParts.join(', ').trim(); // One final aggressive cleanup if (finalAddress) { finalAddress = finalAddress .replace(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/gi, '') .replace(/www\.[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/gi, '') .replace(/https?:\/\/[^\s,]+/gi, '') .replace(/http:\/\/[^\s,]+/gi, '') .replace(/https:\/\/[^\s,]+/gi, '') .replace(/\bContact[^,]*/gi, '') .replace(/\bPhone[^,]*/gi, '') .replace(/\bTel[^,]*/gi, '') .replace(/\bMob[^,]*/gi, '') .replace(/\bMobile[^,]*/gi, '') .replace(/\bEmail[^,]*/gi, '') .replace(/\bE-mail[^,]*/gi, '') .replace(/\bWebsite[^,]*/gi, '') .replace(/\bW\s+www\.[^,]*/gi, '') .replace(/\s*,\s*,/g, ',') .replace(/\s*,\s*,\s*/g, ',') .replace(/\s*,\s*$/g, '') .replace(/^\s*,\s*/g, '') .replace(/\s+/g, ' ') .trim(); } // Final validation - if still contains unwanted patterns, return null if (finalAddress) { const stillHasEmail = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/.test(finalAddress); const stillHasWebsite = /www\.|https?:\/\//.test(finalAddress); const stillHasContact = /\b(Contact|Phone|Tel|Mob|Mobile|Email|E-mail|Website)\b/i.test(finalAddress); if (stillHasEmail || stillHasWebsite || stillHasContact) { // Last resort: try to extract only the address-like parts const safeParts = finalAddress.split(',').map(p => p.trim()).filter(p => { return p && !/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/.test(p) && !/www\.|https?:\/\//.test(p) && !/\b(Contact|Phone|Tel|Mob|Mobile|Email|E-mail|Website)\b/i.test(p); }); finalAddress = safeParts.length > 0 ? safeParts.join(', ') : null; } } } const displayAddress = finalAddress || null; const displayDistrict = extractedDistrict || college.district || null; // Check if we have live data for this college const hasLiveHostel = liveData[college.id]?.hostel; const hasLiveFees = liveData[college.id]?.fees; const hasLiveContact = liveData[college.id]?.contact; // Parse facilities - use database data for parsing (live data will be displayed separately) const facilityText = (college.courses && Array.isArray(college.courses) && college.courses.length > 0 && college.courses[0]?.course_facility) ? college.courses[0].course_facility : (college.facility || null); const parseFacilities = (text) => { const facilities = []; if (!text || typeof text !== 'string' || !text.trim()) { return facilities; } const parts = text.split(/[,;]/).map(p => p.trim()).filter(p => p); for (const part of parts) { if (part.includes(':')) { const [name, value] = part.split(':').map(s => s.trim()); const isAvailable = value && ( value.toLowerCase().includes('yes') || value.toLowerCase().includes('1') || value.toLowerCase() === 'y' ); facilities.push({ name, value, isAvailable, raw: part }); } else { facilities.push({ name: part, value: null, isAvailable: null, raw: part }); } } if (facilities.length === 0 || facilities.every(f => !f.name.includes(':'))) { const lines = text.split(/[\n\r]+/).map(l => l.trim()).filter(l => l); if (lines.length > 0) { facilities.length = 0; for (const line of lines) { if (line.includes(':')) { const [name, value] = line.split(':').map(s => s.trim()); const isAvailable = value && ( value.toLowerCase().includes('yes') || value.toLowerCase().includes('1') || value.toLowerCase() === 'y' ); facilities.push({ name, value, isAvailable, raw: line }); } else { facilities.push({ name: line, value: null, isAvailable: null, raw: line }); } } } } return facilities; }; // Parse facilities from all courses, not just the first one const allCourseFacilities = []; if (college.courses && Array.isArray(college.courses) && college.courses.length > 0) { college.courses.forEach(course => { if (course && course.course_facility && typeof course.course_facility === 'string') { try { const courseFacs = parseFacilities(course.course_facility); if (Array.isArray(courseFacs)) { allCourseFacilities.push(...courseFacs); } } catch (e) { console.error('Error parsing course facility:', e); } } }); } // Also parse from main facility text const mainFacilities = (facilityText && typeof facilityText === 'string') ? parseFacilities(facilityText) : []; const allFacilities = [...allCourseFacilities, ...mainFacilities]; // Remove duplicates based on raw text const uniqueFacilities = []; const seen = new Set(); allFacilities.forEach(fac => { const key = fac.raw || fac.name; if (!seen.has(key)) { seen.add(key); uniqueFacilities.push(fac); } }); // Extract boys and girls hostel information separately let boysHostel = null; let girlsHostel = null; const otherFacilities = []; uniqueFacilities.forEach(fac => { if (!fac || typeof fac !== 'object' || !fac.name) { return; // Skip invalid facility objects } try { const nameLower = (fac.name || '').toLowerCase(); if (nameLower.includes('boys') && (nameLower.includes('hostel') || nameLower.includes('accommodation'))) { boysHostel = fac; } else if (nameLower.includes('girls') && (nameLower.includes('hostel') || nameLower.includes('accommodation'))) { girlsHostel = fac; } else if (nameLower.includes('hostel') || nameLower.includes('accommodation')) { // Generic hostel info (if not specifically boys/girls) if (boysHostel === null) { boysHostel = fac; } else if (girlsHostel === null) { girlsHostel = fac; } } else { // All other facilities (Mess, Transportation, etc.) go to Other Facilities otherFacilities.push(fac); } } catch (e) { console.error('Error processing facility:', fac, e); } }); // Also check facility text directly for boys/girls hostel patterns if (facilityText && typeof facilityText === 'string') { try { const facilityLower = facilityText.toLowerCase(); // Check for "BOYS HOSTEL" or "BOYS HOSTEL: YES/NO" if (facilityLower.includes('boys') && facilityLower.includes('hostel')) { const boysMatch = facilityText.match(/boys\s+hostel[:\s]*([^,\n;]+)/i); if (boysMatch && boysMatch[1]) { boysHostel = { name: 'BOYS HOSTEL', value: boysMatch[1].trim(), isAvailable: boysMatch[1].toLowerCase().includes('yes') || boysMatch[1].toLowerCase().includes('1'), raw: boysMatch[0] }; } } // Check for "GIRLS HOSTEL" or "GIRLS HOSTEL: YES/NO" if (facilityLower.includes('girls') && facilityLower.includes('hostel')) { const girlsMatch = facilityText.match(/girls\s+hostel[:\s]*([^,\n;]+)/i); if (girlsMatch && girlsMatch[1]) { girlsHostel = { name: 'GIRLS HOSTEL', value: girlsMatch[1].trim(), isAvailable: girlsMatch[1].toLowerCase().includes('yes') || girlsMatch[1].toLowerCase().includes('1'), raw: girlsMatch[0] }; } } } catch (e) { console.error('Error parsing facility text for hostel info:', e); } } // Get fee information - prioritize live data, then fees_detail, then course_fee const liveFee = liveData[college.id]?.fees; // Collect all fee information from courses const courseFees = []; const feesDetails = []; if (college.courses && college.courses.length > 0) { college.courses.forEach(course => { if (course.course_fee) { courseFees.push(course.course_fee); } if (course.course_fees_detail) { feesDetails.push(course.course_fees_detail); } }); } // Calculate average fee const avgFee = liveFee || (courseFees.length > 0 ? Math.round(courseFees.reduce((a, b) => a + b, 0) / courseFees.length) : (college.avg_fee_inr || null)); // Parse fees_detail to extract boys and girls fees separately let boysFees = null; let girlsFees = null; let otherFeesDetail = null; if (feesDetails && Array.isArray(feesDetails) && feesDetails.length > 0) { try { const combinedFeesDetail = feesDetails.filter(f => f && typeof f === 'string').join('; '); if (combinedFeesDetail) { // Extract BOYS FEES const boysFeesMatch = combinedFeesDetail.match(/boys\s+fees?[:\s]*([^,\n;]+)/i); if (boysFeesMatch && boysFeesMatch[1]) { boysFees = boysFeesMatch[1].trim(); } // Extract GIRLS FEES const girlsFeesMatch = combinedFeesDetail.match(/girls\s+fees?[:\s]*([^,\n;]+)/i); if (girlsFeesMatch && girlsFeesMatch[1]) { girlsFees = girlsFeesMatch[1].trim(); } // If no specific boys/girls fees found, use the whole text as other fees if (!boysFees && !girlsFees) { otherFeesDetail = combinedFeesDetail; } } } catch (e) { console.error('Error parsing fees detail:', e); // Fallback to showing all fees detail if (feesDetails.length > 0) { otherFeesDetail = feesDetails.filter(f => f && typeof f === 'string').join('; '); } } } return ( { e.currentTarget.style.background = '#eff6ff'; }} onMouseLeave={(e) => { e.currentTarget.style.background = idx % 2 === 0 ? 'white' : '#f9fafb'; }}> {/* Institute Name */} {/* Address - ONLY Address (no district, email, website, contact, city, state, pincode) */} {/* District */} {/* University / Affiliation */} {/* Contact Details */} {/* Website */} {/* Hostel Facility - Only Boys/Girls Hostel Info */} {/* Branch / Department - Show Program/Course Names */} {/* Other Facilities - Show Only Transportation and Mess */} {/* Annual Fee */} ); } catch (error) { console.error('Error processing college:', college?.name || 'Unknown', error); console.error('Error details:', { message: error.message, stack: error.stack, college: college ? { id: college.id, name: college.name, hasCourses: !!college.courses, coursesLength: college.courses?.length } : 'college is null/undefined' }); return ( ); } }); })()}
Institute Name Address District University Contact Website Hostel Branch Facilities Fee (β‚Ή)
{ e.currentTarget.style.background = '#e7f3ff'; }} onMouseLeave={(e) => { e.currentTarget.style.background = idx % 2 === 0 ? 'white' : '#f8f9fa'; }}>
{cleanCollegeName} {(college.city || displayDistrict) && ( ({college.city || displayDistrict}) )}
{college.inst_type && ( {college.inst_type} )} {college.code && (
Code: {college.code}
)}
{displayAddress ? (
{displayAddress}
) : ( Information not available )}
{displayDistrict ? (
{displayDistrict}
) : ( Information not available )}
{college.university && college.university.trim() !== '' && college.university !== 'null' ? (
{college.university.trim()}
) : ( Information not available )}
{/* Show loading indicator while fetching */} {fetchingLiveData[college.id] && !college.contact_no && !college.email && (
Fetching...
)} {/* Live Contact Data */} {hasLiveContact ? (
LIVE
{liveData[college.id].contact.phones && liveData[college.id].contact.phones.length > 0 && ( )} {liveData[college.id].contact.emails && liveData[college.id].contact.emails.length > 0 && ( )}
) : null} {displayContact ? ( ) : college.contact_no ? ( ) : null} {displayEmail ? ( ) : college.email ? ( ) : null} {!hasLiveContact && !displayContact && !college.contact_no && !displayEmail && !college.email && (
Information not available
)}
{(() => { // Format website for display const formatWebsite = (url) => { if (!url) return null; // Remove protocol and www for display let display = url.replace(/^https?:\/\//, '').replace(/^www\./, ''); // Ensure we have a valid domain if (!/^[a-zA-Z0-9][a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(display)) { return null; } // Format href - ensure it has protocol // For domains extracted from email (no www), use https:// directly let href = url; if (!href.startsWith('http://') && !href.startsWith('https://')) { // If it's just a domain (extracted from email), use https:// directly if (!href.startsWith('www.')) { href = 'https://' + href; } else { href = 'https://' + href; } } return { display, href }; }; const websiteInfo = displayWebsite ? formatWebsite(displayWebsite) : null; return websiteInfo ? ( ) : ( Information not available ); })()} {/* Show loading indicator while fetching */} {fetchingLiveData[college.id] && !boysHostel && !girlsHostel && (
Fetching...
)} {/* Display Live Data or Database Data */} {hasLiveHostel ? (
LIVE
{liveData[college.id].hostel}
) : null} {/* Display Boys and Girls Hostel separately */} {(boysHostel || girlsHostel) ? (
{boysHostel ? (
BOYS HOSTEL: {(boysHostel.value && boysHostel.value.trim()) || ((boysHostel.isAvailable !== false) ? 'YES' : 'NO')}
) : null} {girlsHostel ? (
GIRLS HOSTEL: {(girlsHostel.value && girlsHostel.value.trim()) || ((girlsHostel.isAvailable !== false) ? 'YES' : 'NO')}
) : null}
) : !hasLiveHostel && !boysHostel && !girlsHostel && !fetchingLiveData[college.id] ? ( Information not available ) : null}
{college.courses && Array.isArray(college.courses) && college.courses.length > 0 ? (
{college.courses.map((course, cIdx) => (
{course.course_name || course.name || 'N/A'}
))}
) : ( Information not available )}
{(() => { // Filter facilities to show only Transportation and Mess const transportAndMess = otherFacilities.filter(fac => { if (!fac || !fac.name) return false; const nameLower = fac.name.toLowerCase(); return nameLower.includes('transport') || nameLower.includes('mess'); }); return transportAndMess.length > 0 ? (
{transportAndMess.map((fac, fIdx) => (
{fac.name} {fac.value && ( : {fac.value} )}
))}
) : ( Information not available ); })()}
{/* Live Fee Data */} {hasLiveFees ? (
LIVE
β‚Ή{liveData[college.id].fees.toLocaleString()}
) : null} {/* Display Boys and Girls Fees separately */} {(boysFees || girlsFees || otherFeesDetail) ? (
{boysFees ? (
BOYS FEES: {boysFees}
) : null} {girlsFees ? (
GIRLS FEES: {girlsFees}
) : null} {otherFeesDetail && !boysFees && !girlsFees ? (
Fee Details:
{otherFeesDetail}
) : null}
) : null} {/* Average Fee */} {avgFee && !hasLiveFees && !boysFees && !girlsFees && !otherFeesDetail ? (
β‚Ή{avgFee.toLocaleString()}
{college.courses && college.courses.length > 1 && courseFees.length > 0 && (
(avg of {courseFees.length} courses)
)}
) : !hasLiveFees && !avgFee && !boysFees && !girlsFees && !otherFeesDetail && !fetchingLiveData[college.id] ? ( Information not available ) : null}
Error loading college data: {college?.name || 'Unknown'}
{error.message || 'Unknown error'}
{/* Pagination Controls */} {collegesByCourse.length > collegePageSize && (
Showing of {collegesByCourse.length} colleges
Page {collegePage} of {Math.ceil(collegesByCourse.length / collegePageSize)}
)}
)} {/* No Results */} {!isLoadingCollegesByCourse && selectedCourseForColleges && collegesByCourse.length === 0 && (
πŸ”

No colleges found

No colleges found offering {selectedCourseForColleges}

)}
{/* Notifications Section */} {notifications.length > 0 && (

Latest Notifications

{/* Latest Notification - Prominent Display */} {notifications.length > 0 && notifications[0] && (
{ e.preventDefault(); e.stopPropagation(); try { if (typeof openNotificationBrochure === 'function') { openNotificationBrochure(notifications[0], 0); } else { const modal = document.getElementById('brochureModal'); const modalTitle = document.getElementById('brochureModalTitle'); if (modal && modalTitle) { modalTitle.innerHTML = `${notifications[0].title || 'Notification'}`; if (typeof loadBrochureContent === 'function') { loadBrochureContent(notifications[0], 0); } modal.style.display = 'flex'; } else if (notifications[0].url && notifications[0].url !== '#') { window.open(notifications[0].url, '_blank'); } } } catch (err) { console.error('Error opening notification:', err); if (notifications[0].url && notifications[0].url !== '#') { window.open(notifications[0].url, '_blank'); } } }} style={{ background: 'linear-gradient(135deg, #fbbf24 0%, #f59e0b 100%)', borderRadius: '12px', padding: '20px', marginBottom: '20px', cursor: 'pointer', transition: 'all 0.3s ease', boxShadow: '0 6px 20px rgba(245, 158, 11, 0.4)', border: '2px solid rgba(255, 255, 255, 0.3)' }} onMouseEnter={(e) => { e.currentTarget.style.transform = 'translateY(-2px)'; e.currentTarget.style.boxShadow = '0 8px 28px rgba(245, 158, 11, 0.5)'; }} onMouseLeave={(e) => { e.currentTarget.style.transform = 'translateY(0)'; e.currentTarget.style.boxShadow = '0 6px 20px rgba(245, 158, 11, 0.4)'; }} >
Latest Notification {notifications[0].date || 'N/A'}
{notifications[0].title || 'Notification'}
Click to view full details
)}
{notifications.slice(1, 7).map((n, i) => (
{ e.preventDefault(); e.stopPropagation(); // Try to use the global openNotificationBrochure function try { if (typeof openNotificationBrochure === 'function') { openNotificationBrochure(n, i); } else { // Fallback: manually open the modal const modal = document.getElementById('brochureModal'); const modalTitle = document.getElementById('brochureModalTitle'); if (modal && modalTitle) { modalTitle.innerHTML = `${n.title || 'Notification'}`; if (typeof loadBrochureContent === 'function') { loadBrochureContent(n, i); } else { // Manual content loading const modalContent = document.getElementById('brochureContent'); const downloadLink = document.getElementById('brochureDownloadLink'); if (modalContent) { if (n.brochure_file) { modalContent.innerHTML = `
`; if (downloadLink) { downloadLink.href = n.brochure_file; downloadLink.style.display = 'inline-block'; } } else { modalContent.innerHTML = `

${n.title || 'Notification'}

${n.date || 'N/A'}

This notification contains important information regarding the admission process.

${n.url && n.url !== '#' ? `

External Link: View Full Notification

` : '' } ${n.brochure_title ? `

Brochure: ${n.brochure_title}

` : ''}
`; if (downloadLink) { downloadLink.style.display = 'none'; } } } } modal.style.display = 'flex'; } else if (n.url && n.url !== '#') { window.open(n.url, '_blank'); } } } catch (err) { console.error('Error opening notification:', err); if (n.url && n.url !== '#') { window.open(n.url, '_blank'); } } }} style={{ background: 'white', borderRadius: '12px', padding: '16px', cursor: 'pointer', transition: 'all 0.3s ease', boxShadow: '0 4px 12px rgba(0,0,0,0.1)', border: '1px solid rgba(255,255,255,0.2)' }} onMouseEnter={(e) => { e.currentTarget.style.transform = 'translateY(-4px)'; e.currentTarget.style.boxShadow = '0 8px 24px rgba(0,0,0,0.15)'; }} onMouseLeave={(e) => { e.currentTarget.style.transform = 'translateY(0)'; e.currentTarget.style.boxShadow = '0 4px 12px rgba(0,0,0,0.1)'; }} >
{n.date || 'N/A'}
{n.title || 'Notification'}
Click to view details
))}
)} {/* Approved Boards Section */}

Approved Education Boards

{showApprovedBoards && (
{/* Filters */}
setBoardSearch(e.target.value)} placeholder="Search by board name or state..." style={{ width: '100%', padding: '10px', borderRadius: '8px', border: 'none', fontSize: '14px', background: 'white' }} />
{/* Boards List */} {isLoadingBoards ? (
Loading approved boards...
) : filteredBoards.length === 0 ? (
No boards found matching your criteria.
) : (
{filteredBoards.map((board) => (
{ e.currentTarget.style.transform = 'translateY(-4px)'; e.currentTarget.style.boxShadow = '0 8px 24px rgba(0,0,0,0.15)'; }} onMouseLeave={(e) => { e.currentTarget.style.transform = 'translateY(0)'; e.currentTarget.style.boxShadow = '0 4px 12px rgba(0,0,0,0.1)'; }} >
{board.state_ut || 'N/A'}
{board.board_name || 'N/A'}
{board.relevant_acts && (
Acts: {board.relevant_acts}
)} {board.website && (
{ e.currentTarget.style.textDecoration = 'underline'; }} onMouseLeave={(e) => { e.currentTarget.style.textDecoration = 'none'; }} > {board.website}
)}
))}
)}
)}
{/* Brochure Modal for Rank Finder Page */}

Notification Brochure

Loading brochure...
Download PDF
); } function CoursesPage({ catalog, collegesByField, initialFilters }) { const [qualification, setQualification] = React.useState(initialFilters.qualification || ''); const [stream, setStream] = React.useState(initialFilters.stream || ''); const [level, setLevel] = React.useState(initialFilters.level || ''); const [field, setField] = React.useState(initialFilters.field || ''); const [exam, setExam] = React.useState(initialFilters.exam || ''); const [college, setCollege] = React.useState(initialFilters.college || ''); // Search functionality removed - use Rank Finder page instead const resultsRef = React.useRef(null); const [flash, setFlash] = React.useState(false); // Pagination for Course Catalog const [coursePage, setCoursePage] = React.useState(1); const [coursePageSize, setCoursePageSize] = React.useState(10); // Load admissions data for rounds on the Courses page const [admissions, setAdmissions] = React.useState(() => window.__admissionsData__ || null); React.useEffect(() => { if (!admissions) { fetch('api/admissions.php') .then(r => { if (r.ok) { return safeJsonParse(r); } return null; }) .then(data => { if (data) { setAdmissions(data); window.__admissionsData__ = data; } }) .catch(() => {}); } }, []); const roundsRaw = admissions?.ug_engineering?.rounds || []; const rounds = React.useMemo(() => roundsRaw.map(r => ({ id: r.id, name: r.name || (r.seq ? `Round-${r.seq}` : `Round-${r.id}`), status: r.status, application: { start: (r.application && r.application.start) || r.application_start || '', end: (r.application && r.application.end) || r.application_end || '' }, merit_declaration: r.merit_declaration || r.merit_date || '', choice_filling: { start: (r.choice_filling && r.choice_filling.start) || r.choice_start || '', end: (r.choice_filling && r.choice_filling.end) || r.choice_end || '' }, seat_allotment: r.seat_allotment || r.allotment_date || '', reporting: { start: (r.reporting && r.reporting.start) || r.reporting_start || '', end: (r.reporting && r.reporting.end) || r.reporting_end || '' }, })), [roundsRaw]); // downloadEligibilityPdf function removed - use Rank Finder page instead // Search-related functions removed - use Rank Finder page instead // collegesByField is provided via props // Full option lists regardless of selection; filtering applies only after selection const allFields = React.useMemo(() => Array.from(new Set(catalog.map(c => c.field))).sort(), [catalog]); const allLevels = React.useMemo(() => Array.from(new Set(catalog.map(c => c.level))).sort(), [catalog]); const allExams = React.useMemo(() => Array.from(new Set(catalog.flatMap(c => c.exams))).filter(Boolean).sort(), [catalog]); const allColleges = React.useMemo(() => { const out = new Set(); Object.values(collegesByField).forEach(list => list.forEach(name => out.add(name))); return Array.from(out).sort(); }, [collegesByField]); const filteredCatalog = React.useMemo(() => { const norm = (s) => (s || '').toString().trim().toLowerCase(); const qualificationNorm = norm(qualification); const streamNorm = norm(stream); const levelNorm = norm(level); const fieldNorm = norm(field); const examNorm = norm(exam); const collegeNorm = norm(college); const list = catalog.filter(item => { const itemEligibility = norm(item.eligibility); const itemLevel = norm(item.level); const itemField = norm(item.field); const itemStream = norm(item.stream); const itemExams = Array.isArray(item.exams) ? item.exams.map(norm) : []; // Qualification mapping: 10th -> Diploma, 12th -> UG, UG -> PG if (qualificationNorm) { const qualToLevels = { '10th': ['diploma'], '12th': ['ug'], 'ug': ['pg'] }; const allowedLevels = qualToLevels[qualificationNorm] || []; const eligibleByLevel = allowedLevels.length === 0 || allowedLevels.includes(itemLevel); const eligibleByFlag = (qualificationNorm === '12th' && (itemEligibility === 'ug' || itemEligibility.includes('12'))) || (qualificationNorm === '10th' && (itemEligibility.includes('10'))) || (qualificationNorm === 'ug' && (itemEligibility === 'pg' || itemEligibility.includes('bachelor') || itemEligibility.includes('ug'))); if (!(eligibleByLevel || eligibleByFlag)) return false; // Stream restriction (only applies for 12th) if (qualificationNorm === '12th' && streamNorm) { if (itemStream && itemStream !== streamNorm) return false; // For items without explicit stream, allow common cross-stream UG programs like BBA/B.Com/B.A. if (!itemStream && streamNorm && ['commerce','arts'].includes(streamNorm)) { const name = norm(item.name); const cross = ['bba','b.com','b.a','bca']; if (!cross.some(k => name.includes(k))) { // Keep engineering/pharmacy etc out when choosing non-science stream if (['engineering','pharmacy','architecture','it','computer'].some(k => name.includes(k))) return false; } } } } if (levelNorm && itemLevel !== levelNorm) return false; if (fieldNorm && itemField !== fieldNorm) return false; if (examNorm && !itemExams.includes(examNorm)) return false; if (collegeNorm) { const pool = (collegesByField[item.field] || []).map(norm); if (!pool.includes(collegeNorm)) return false; } return true; }); // Rank/percentage based soft ordering using available cutoffs from admissions (loaded on Home) let sorted = list.slice(); const r = parseInt(rank, 10); const p = parseFloat(percent); if (!isNaN(r) || !isNaN(p) || cityScope !== 'any' || homeCity) { // Fetch admissions cutoffs from window (HomeSections stores it on window for reuse) const admissions = window.__admissionsData__; const cutoffs = admissions?.ug_engineering?.cutoffs || []; const byCourse = new Map(); cutoffs.forEach(co => { const key = (co.course || '').toLowerCase(); if (!byCourse.has(key)) byCourse.set(key, []); byCourse.get(key).push(co); }); sorted.sort((a, b) => { const ak = (a.name || '').toLowerCase(); const bk = (b.name || '').toLowerCase(); const aCut = byCourse.get(ak) || []; const bCut = byCourse.get(bk) || []; const aScore = scoreCourse(aCut, r, p, homeCity, cityScope); const bScore = scoreCourse(bCut, r, p, homeCity, cityScope); return bScore - aScore; }); } return sorted; }, [catalog, qualification, stream, level, field, exam, college, collegesByField, rank, percent, homeCity, cityScope]); // Reset Course Catalog pagination on relevant filter changes React.useEffect(() => { setCoursePage(1); }, [qualification, stream, level, field, exam, college, catalog.length]); // runEligibilitySearch function removed - use Rank Finder page instead return (
{/* Professional Header */}

Available Courses

Comprehensive course catalog with detailed information about eligibility, duration, and entrance requirements

{/* Search functionality removed - use Rank Finder page instead */} {/* Course Filters */}

Filter Courses

{/* Search field removed - use Rank Finder page instead */} {/* Active Filters */} {(qualification || stream || level || field || exam || college) && (
Active Filters:
{qualification && ( {qualification} )} {stream && ( {stream} )} {level && ( {level} )} {field && ( {field} )} {college && ( {college} )} {exam && ( {exam} )}
)}
{filteredCatalog.length} courses found
{/* Main Content Area */}
{/* Courses Table */}

Course Catalog

Browse through all available courses and their details
{/* Catalog Pagination Controls - Top */}
{(() => { const total = filteredCatalog.length; const totalPages = Math.max(1, Math.ceil(total / coursePageSize)); return (
Page {coursePage} / {totalPages}
); })()}
{filteredCatalog.length === 0 ? ( ) : (() => { const total = filteredCatalog.length; const startIdx = (coursePage - 1) * coursePageSize; const endIdx = startIdx + coursePageSize; const visible = filteredCatalog.slice(startIdx, endIdx); return visible.map(item => ( )); })()}
Course Name Level Field Duration Eligibility Exams

No courses found

Try adjusting your filters or clearing them to see more results.

{item.name} {item.level} {item.field} {`${inferDisplayDuration(item.name, item.level, item.field, item.durationYears)} Years`} {item.eligibility}{item.stream ? ` (${item.stream})` : ''} {item.exams.length ? item.exams.join(', ') : 'β€”'}
{/* Catalog Pagination Controls - Bottom */}
{filteredCatalog.length.toLocaleString()} results
{(() => { const total = filteredCatalog.length; const totalPages = Math.max(1, Math.ceil(total / coursePageSize)); return (
Page {coursePage} of {totalPages}
); })()}
{/* Eligibility search removed - use Rank Finder page instead */} {/* Admission Timeline */}

Admission Timeline

Important dates and deadlines for admission rounds {(() => { const vac = rounds.find(r => (r.name||'').toLowerCase().includes('vacant')); if (!vac) return null; return ( <> ); })()}
{rounds.map(r => (
{r.name}
{r.status}
Application Period {r.application.start} β†’ {r.application.end}
Merit Declaration {r.merit_declaration}
Choice Filling {r.choice_filling.start} β†’ {r.choice_filling.end}
Seat Allotment {r.seat_allotment}
Reporting Period {r.reporting.start} β†’ {r.reporting.end}
Download Round
))} {rounds.length === 0 && (

No admission rounds available

Check back later for updated admission timeline.

)}
); } function scoreCourse(courseCutoffs, rank, percent, homeCity, cityScope) { // Higher score is better match let score = 0; // Basic closeness by rank (lower closing rank is more competitive) if (!isNaN(rank)) { const best = Math.min(...courseCutoffs.map(co => parseInt(co.closing_rank, 10)).filter(n => !isNaN(n)), Infinity); if (best !== Infinity) { // If user's rank is better than or close to best closing rank, bump if (rank <= best) score += 50; const diff = Math.abs(rank - best); score += Math.max(0, 30 - Math.log10(diff + 1) * 10); } } // Percentage heuristic (if present) if (!isNaN(percent)) { score += Math.min(20, Math.max(0, (percent - 50))); // small boost for higher % } // City preference if (homeCity && cityScope !== 'any') { const anyCity = (arr, city) => arr.some(co => (co.city || '').toLowerCase() === city.toLowerCase()); const hasHome = anyCity(courseCutoffs, homeCity); if (cityScope === 'within') score += hasHome ? 20 : -20; if (cityScope === 'outside') score += hasHome ? -20 : 10; } return score; } // Unused search function removed - use Rank Finder page instead // Simple hash router and data loader function useHashRoute() { const [route, setRoute] = React.useState(window.location.hash || '#/'); React.useEffect(() => { const onChange = () => setRoute(window.location.hash || '#/'); window.addEventListener('hashchange', onChange); return () => window.removeEventListener('hashchange', onChange); }, []); return route; } function parseHashParams(hash) { const idx = hash.indexOf('?'); if (idx === -1) return {}; const query = hash.slice(idx + 1); const params = new URLSearchParams(query); return { q: params.get('q') || '', qualification: params.get('qualification') || '', stream: params.get('stream') || '', level: params.get('level') || '', field: params.get('field') || '', exam: params.get('exam') || '', college: params.get('college') || '', }; } function HomePage() { const [homeQuery, setHomeQuery] = React.useState(''); const goToCourses = (extra = {}) => { const params = new URLSearchParams(); if (homeQuery.trim()) params.set('q', homeQuery.trim()); Object.entries(extra).forEach(([k, v]) => { if (v) params.set(k, v); }); window.location.hash = `#/courses?${params.toString()}`; }; return ( ); } // Brochure modal functions function openNotificationBrochure(notification, index) { console.log('Opening brochure for notification:', notification); // Set modal title document.getElementById('brochureModalTitle').innerHTML = `${notification.title}`; // Show modal document.getElementById('brochureModal').style.display = 'flex'; // Load brochure content loadBrochureContent(notification, index); } function closeBrochureModal() { document.getElementById('brochureModal').style.display = 'none'; document.getElementById('brochureContent').innerHTML = '
Loading brochure...
'; } function loadBrochureContent(notification, index) { const contentDiv = document.getElementById('brochureContent'); const downloadLink = document.getElementById('brochureDownloadLink'); // Check if notification has brochure if (notification.brochure_file) { // Show PDF embed contentDiv.innerHTML = `
`; // Set download link downloadLink.href = notification.brochure_file; downloadLink.style.display = 'inline-block'; } else { // Show notification detail page content contentDiv.innerHTML = `

${notification.title}

${notification.date}

This notification contains important information regarding the admission process. Please read all details carefully and take necessary action as required.

${notification.url && notification.url !== '#' ? `

External Link: View Full Notification

` : '' }
No brochure PDF available for this notification.
`; // Hide download link downloadLink.style.display = 'none'; } } function HomeSections() { const [admissions, setAdmissions] = React.useState(null); const [cutoffs2024, setCutoffs2024] = React.useState([]); // Last Admitted Rank data const [lastRankYear, setLastRankYear] = React.useState(2024); const [lastRankRows, setLastRankRows] = React.useState([]); const [lastRankLoading, setLastRankLoading] = React.useState(false); React.useEffect(() => { fetch('api/admissions.php') .then(safeJsonParse) .then(setAdmissions) .catch(() => setAdmissions(null)); // Add click outside to close modal const handleClickOutside = (e) => { const modal = document.getElementById('brochureModal'); if (modal && e.target === modal) { closeBrochureModal(); } }; // Add escape key to close modal const handleEscapeKey = (e) => { if (e.key === 'Escape') { const modal = document.getElementById('brochureModal'); if (modal && modal.style.display === 'flex') { closeBrochureModal(); } } }; document.addEventListener('click', handleClickOutside); document.addEventListener('keydown', handleEscapeKey); return () => { document.removeEventListener('click', handleClickOutside); document.removeEventListener('keydown', handleEscapeKey); }; }, []); // Removed: last year rounds (Round-1/2/3) section and its fetch React.useEffect(() => { fetch('api/admissions.php?year=2024') .then(r => { if (r.ok) { return safeJsonParse(r); } return null; }) .then(data => setCutoffs2024(data?.ug_engineering?.cutoffs || [])) .catch(() => setCutoffs2024([])); }, []); // Load last admitted rank data React.useEffect(() => { let cancelled = false; setLastRankLoading(true); fetch('api/last_admitted_rank.php?format=json&year=' + encodeURIComponent(lastRankYear)) .then(r => { if (r.ok) { return safeJsonParse(r); } return []; }) .then(rows => { if (!cancelled) { // Check if response is an error object if (rows && typeof rows === 'object' && !Array.isArray(rows) && rows.error) { console.error('Last admitted rank API error:', rows.error); alert('Error loading last admitted rank data: ' + rows.error); setLastRankRows([]); } else { const data = Array.isArray(rows) ? rows : []; console.log('Last admitted rank loaded:', data.length, 'rows for year', lastRankYear); if (data.length > 0) { console.log('Sample row:', data[0]); } setLastRankRows(data); } } }) .catch(err => { if (!cancelled) { console.error('Failed to fetch last admitted rank:', err); setLastRankRows([]); } }) .finally(() => { if (!cancelled) setLastRankLoading(false); }); return () => { cancelled = true; }; }, [lastRankYear]); const scheme = admissions?.scheme || 'Admissions'; const ug = admissions?.ug_engineering; const roundsRaw = ug?.rounds || []; const notifications = ug?.notifications || []; const seatMatrix = ug?.seat_matrix || []; const cutoffs = ug?.cutoffs || []; // Load round-wise details from cutoff and allotment tables const [roundDetails, setRoundDetails] = React.useState([]); React.useEffect(() => { fetch('api/round_details.php') .then(r => { if (r.ok) { return safeJsonParse(r); } return []; }) .then(data => setRoundDetails(data)) .catch(() => setRoundDetails([])); }, []); // Pagination for Sample Cut-offs const [cutPage, setCutPage] = React.useState(1); const [cutPageSize, setCutPageSize] = React.useState(10); const cutTotal = cutoffs.length; const cutPages = Math.max(1, Math.ceil(cutTotal / cutPageSize)); const cutStart = (cutPage - 1) * cutPageSize; const cutEnd = cutStart + cutPageSize; const cutVisible = cutoffs.slice(cutStart, cutEnd); // Normalize rounds to expected nested structure const rounds = React.useMemo(() => roundsRaw.map(r => ({ id: r.id, name: r.name, status: r.status, application: { start: (r.application && r.application.start) || r.application_start || '', end: (r.application && r.application.end) || r.application_end || '' }, merit_declaration: r.merit_declaration || r.merit_date || '', choice_filling: { start: (r.choice_filling && r.choice_filling.start) || r.choice_start || '', end: (r.choice_filling && r.choice_filling.end) || r.choice_end || '' }, seat_allotment: r.seat_allotment || r.allotment_date || '', reporting: { start: (r.reporting && r.reporting.start) || r.reporting_start || '', end: (r.reporting && r.reporting.end) || r.reporting_end || '' }, })), [roundsRaw]); React.useEffect(() => { if (admissions) window.__admissionsData__ = admissions; }, [admissions]); // Pagination for Last Admitted Rank const [lastRankPage, setLastRankPage] = React.useState(1); const [lastRankPageSize, setLastRankPageSize] = React.useState(20); const lastRankTotal = lastRankRows.length; const lastRankPages = Math.max(1, Math.ceil(lastRankTotal / lastRankPageSize)); const lastRankStart = (lastRankPage - 1) * lastRankPageSize; const lastRankEnd = lastRankStart + lastRankPageSize; const lastRankVisible = lastRankRows.slice(lastRankStart, lastRankEnd); React.useEffect(() => { setLastRankPage(1); }, [lastRankYear, lastRankRows]); // Pagination for Cut-offs (2024) const [cut24Page, setCut24Page] = React.useState(1); const [cut24PageSize, setCut24PageSize] = React.useState(25); const cut24Total = cutoffs2024.length; const cut24Pages = Math.max(1, Math.ceil(cut24Total / cut24PageSize)); const cut24Start = (cut24Page - 1) * cut24PageSize; const cut24End = cut24Start + cut24PageSize; const cut24Visible = cutoffs2024.slice(cut24Start, cut24End); React.useEffect(() => { setCut24Page(1); }, [cutoffs2024]); return ( <>
{/* Decorative floating shapes */}
{ e.currentTarget.style.transform = 'scale(1.1) rotate(5deg)'; e.currentTarget.style.boxShadow = '0 15px 50px rgba(0, 0, 0, 0.3), 0 0 80px rgba(255, 255, 255, 0.15), inset 0 0 40px rgba(255, 255, 255, 0.4)'; }} onMouseLeave={(e) => { e.currentTarget.style.transform = 'scale(1) rotate(0deg)'; e.currentTarget.style.boxShadow = '0 10px 40px rgba(0, 0, 0, 0.25), 0 0 60px rgba(255, 255, 255, 0.1), inset 0 0 30px rgba(255, 255, 255, 0.3)'; }}> {/* Shine effect overlay */}

Get My Admission (Unofficial Tool)

Plan β€’ Apply β€’ Succeed

Empowering Dreams, Shaping Futures

I'm in 12th
Trusted by 50,000+ applicants across Gujarat
{/* Highlights */}
Smart Search
Advanced filtering by qualification, stream, city preference, and eligibility criteria.
Cut-off Insights
Historical closing ranks and eligibility scores to help you make informed decisions.
Live Timeline
Real-time admission rounds tracking with important dates and notifications.
College Profiles
Detailed college information with photos, courses, fees, and contact details.
{/* Stats */}
1000+
Programs
200+
Colleges
10+
Fields
{(rounds.length || 4) + ''}
Active Rounds

Admission Rounds Timeline

{roundDetails.length > 0 ? roundDetails.map(round => (
{round.round_name}
{round.status || 'Active'}
Application
{round.application_start} β†’ {round.application_end}
Merit
{round.merit_declaration}
Choice Filling
{round.choice_start} β†’ {round.choice_end}
Allotment
{round.seat_allotment}
Reporting
{round.reporting_start} β†’ {round.reporting_end}
Round Statistics
Total Cutoffs: {round.total_cutoffs || 0}
Total Allotments: {round.total_allotments || 0}
Vacant Seats: {round.vacant_seats || 0}
Download Round
)) : rounds.map(r => (
{r.name}
{r.status}
Application
{r.application.start} β†’ {r.application.end}
Merit
{r.merit_declaration}
Choice Filling
{r.choice_filling.start} β†’ {r.choice_filling.end}
Allotment
{r.seat_allotment}
Reporting
{r.reporting.start} β†’ {r.reporting.end}
Download Round
))}
{/* Removed: Last Year Rounds (2024) section */}

Latest Notifications

{notifications.length > 0 ? (
{notifications.map((n, i) => (
openNotificationBrochure(n, i)} style={{cursor: 'pointer'}} >
{n.date}
{n.title}
Click to view brochure
))}
) : (

No notifications available at the moment.

Check back later for updates!

)}
{/* Last Admitted Rank */}

Last Admitted Rank

setLastRankYear(parseInt(e.target.value,10)||lastRankYear)} style={{width:100}} />
{lastRankLoading ? (
Loading…
) : lastRankRows.length === 0 ? (
No data available for year {lastRankYear}. Please try selecting a different year.
Check browser console (F12) for details.
) : ( {lastRankVisible.map((r, i) => ( ))}
College Course OPEN SC ST SEBC EWS ESM JEE
H Rank Merit H Rank Merit H Rank Merit H Rank Merit H Rank Merit H Rank Merit Open AI Rank TFWS AI Rank
{r.college_name || 'β€”'} {r.course_name || 'β€”'} {r.open_hrank != null && r.open_hrank !== '' ? r.open_hrank : 'β€”'} {r.open_merit != null && r.open_merit !== '' ? r.open_merit : 'β€”'} {r.sc_hrank != null && r.sc_hrank !== '' ? r.sc_hrank : 'β€”'} {r.sc_merit != null && r.sc_merit !== '' ? r.sc_merit : 'β€”'} {r.st_hrank != null && r.st_hrank !== '' ? r.st_hrank : 'β€”'} {r.st_merit != null && r.st_merit !== '' ? r.st_merit : 'β€”'} {r.sebc_hrank != null && r.sebc_hrank !== '' ? r.sebc_hrank : 'β€”'} {r.sebc_merit != null && r.sebc_merit !== '' ? r.sebc_merit : 'β€”'} {r.ews_hrank != null && r.ews_hrank !== '' ? r.ews_hrank : 'β€”'} {r.ews_merit != null && r.ews_merit !== '' ? r.ews_merit : 'β€”'} {r.esm_hrank != null && r.esm_hrank !== '' ? r.esm_hrank : 'β€”'} {r.esm_merit != null && r.esm_merit !== '' ? r.esm_merit : 'β€”'} {r.jee_open_ai_rank != null && r.jee_open_ai_rank !== '' ? r.jee_open_ai_rank : 'β€”'} {r.jee_tfws_ai_rank != null && r.jee_tfws_ai_rank !== '' ? r.jee_tfws_ai_rank : 'β€”'}
)}
{(lastRankTotal > 0) && (
{lastRankTotal.toLocaleString()} results
Page {lastRankPage} / {lastRankPages}
)}

Seat Matrix

{seatMatrix.map((s, i) => ( ))}
FieldLevelSeats
{s.field}{s.level}{s.seats.toLocaleString()}
{cutoffs2024.length > 0 && (

Cut-offs (2024)

{cut24Visible.map((c, i) => ( ))}
College Course Category Round Closing Rank
{c.college} {c.course} {c.category} {c.round} {c.closing_rank}
{cut24Total.toLocaleString()} results
Page {cut24Page} / {cut24Pages}
)}

Sample Cut-offs

{cutVisible.map((c, i) => ( ))}
CollegeCourseCategoryRoundClosing Rank
{c.college}{c.course}{c.category}{c.round}{c.closing_rank}
{cutTotal.toLocaleString()} results
Page {cutPage} / {cutPages}
{/* CTA Banner */}

Ready to explore your options?

Join thousands of students who have found their perfect college match. Start your journey to higher education today!

Start Exploring Find After 12th
{/* Brochure Modal */}

Notification Brochure

Loading brochure...
Download PDF
); } async function downloadRoundPdf(roundId, roundName) { try { const res = await fetch(`api/export_round.php?format=json&round_id=${roundId}`); if (!res.ok) throw new Error('Failed to load round data'); const rows = await safeJsonParse(res); const head = [['College','Program','Category','Board','Open Rank','Closing Rank']]; const body = rows.map(r => [ r.college||'', r.program||'', r.category||'', r.exam||'', (r.open_rank && r.open_rank > 0) ? r.open_rank : 'β€”', (r.closing_rank && r.closing_rank > 0) ? r.closing_rank : 'β€”' ]); const { jsPDF } = window.jspdf; const doc = new jsPDF({ orientation:'landscape', unit:'pt', format:'a4' }); // Title styling: dark text on white background doc.setTextColor(34, 34, 34); doc.setFontSize(16); doc.text(`${roundName} Cut-offs`, 40, 40); doc.autoTable({ startY: 60, head, body, styles:{ fontSize:9 }, headStyles:{ fillColor:[37,99,235], textColor:[255,255,255], fontStyle:'bold', halign:'center' } }); doc.save(`${roundName.replace(/\s+/g,'_')}_Cutoffs.pdf`); } catch (e) { alert('Unable to generate PDF for '+roundName); } } async function downloadLastYearMetricsPdf() { try { const year = new Date().getFullYear() - 1; const res = await fetch(`api/export_metrics.php?format=json&year=${year}`); if (!res.ok) throw new Error('Failed to load metrics'); const rows = await safeJsonParse(res); // Build summary (scheme / year / round) from dataset const uniq = (arr) => Array.from(new Set(arr.filter(Boolean))); const schemes = uniq(rows.map(r => r.scheme_name)); const years = uniq(rows.map(r => r.scheme_year)); const rounds = uniq(rows.map(r => r.round_name)); // Table should exclude scheme_name, scheme_year, round_name const head = [[ 'college_code','college_name','course_name','exam_name','category_code','metric','value' ]]; const body = rows.map(r => [ r.college_code||'', r.college_name||'', r.course_name||'', r.exam_name||'', r.category_code||'', r.metric||'', String(r.value ?? '') ]); const { jsPDF } = window.jspdf; const doc = new jsPDF({ orientation:'landscape', unit:'pt', format:'a4' }); doc.setTextColor(34, 34, 34); doc.setFontSize(16); doc.text(`Cutoff Metrics ${year}`, 40, 40); // Summary line below title doc.setFontSize(11); const summary = `Scheme: ${schemes.join(', ') || 'β€”'} | Year: ${years.join(', ') || 'β€”'} | Round: ${rounds.join(', ') || 'β€”'}`; doc.text(summary, 40, 58); // Table doc.autoTable({ startY: 76, head, body, styles:{ fontSize:9, cellWidth:'wrap' }, headStyles:{ fillColor:[37,99,235], textColor:[255,255,255], fontStyle:'bold', halign:'center' }, columnStyles:{ 1:{ cellWidth:200 }, 2:{ cellWidth:170 } } }); doc.save(`cutoff_metrics_${year}.pdf`); } catch (e) { alert('Unable to generate PDF'); } } async function downloadLastAdmittedRankPdf() { try { const year = new Date().getFullYear() - 1; const url = `api/export_last_rank.php?format=json&year=${encodeURIComponent(year)}`; const res = await fetch(url); if (!res.ok) { const txt = await res.text().catch(() => ''); throw new Error(`HTTP ${res.status}: ${txt || 'Failed to load last admitted rank'}`); } let rows; try { rows = await safeJsonParse(res); } catch (e) { const txt = await res.text().catch(() => ''); throw new Error(`Invalid JSON from server. ${txt.slice(0,300)}`); } const { jsPDF } = window.jspdf; const doc = new jsPDF({ orientation:'landscape', unit:'pt', format:'a3' }); // Strong brand header band + high-contrast title const pageWidth = doc.internal.pageSize.getWidth(); doc.setFillColor(37, 99, 235); // brand blue doc.rect(0, 0, pageWidth, 72, 'F'); doc.setTextColor(255, 255, 255); try { doc.setFont('helvetica', 'bold'); } catch (_) {} doc.setFontSize(20); doc.text(`Last Admitted Rank β€’ A.Y. ${year}-${String(year+1).slice(-2)}`, 40, 46); const head = [[ 'Institute', 'Course', 'GUJ OPEN H_Rank','GUJ OPEN Merit', 'GUJ SC H_Rank','GUJ SC Merit', 'GUJ ST H_Rank','GUJ ST Merit', 'GUJ SEBC H_Rank','GUJ SEBC Merit', 'GUJ EWS H_Rank','GUJ EWS Merit', 'GUJ ESM H_Rank','GUJ ESM Merit', 'JEE OPEN AI Rank','JEE TFWS AI Rank' ]]; const body = rows.map(r => [ r.institute_name || '', r.course || '', fmt(r.guj_open_hrank), fmt(r.guj_open_merit_marks), fmt(r.guj_sc_hrank), fmt(r.guj_sc_merit_marks), fmt(r.guj_st_hrank), fmt(r.guj_st_merit_marks), fmt(r.guj_sebc_hrank), fmt(r.guj_sebc_merit_marks), fmt(r.guj_ews_hrank), fmt(r.guj_ews_merit_marks), fmt(r.guj_esm_hrank), fmt(r.guj_esm_merit_marks), fmt(r.jee_open_last_rank), fmt(r.jee_tfws_last_rank) ]); function fmt(v){ return (v === null || v === undefined || v === '') ? 'β€”' : String(v); } doc.autoTable({ startY: 84, head, body, styles:{ fontSize:8, cellPadding:3 }, headStyles:{ fillColor:[37,99,235], textColor:[255,255,255], fontStyle:'bold', halign:'center' }, columnStyles:{ 0:{ cellWidth:220 }, 1:{ cellWidth:170 } } }); doc.save(`last_admitted_rank_${year}.pdf`); } catch (e) { console.error(e); alert('Unable to generate Last Admitted Rank PDF: ' + (e && e.message ? e.message : 'unknown error')); } } async function downloadStudentListPdf(roundId = null) { try { const url = roundId ? `api/export_student_list.php?format=json&round_id=${roundId}` : 'api/export_student_list.php?format=json'; const res = await fetch(url); if (!res.ok) { throw new Error('Failed to load student list data'); } const rows = await safeJsonParse(res); const { jsPDF } = window.jspdf; const doc = new jsPDF({ orientation:'landscape', unit:'pt', format:'a4' }); // Header styling doc.setTextColor(34, 34, 34); doc.setFontSize(16); const title = roundId ? `Student Admission List - Round ${roundId}` : 'Student Admission List'; doc.text(title, 40, 40); const head = [['Sr.No', 'Roll No', 'Student Name', 'College', 'Course', 'Category', 'Rank', 'Reporting Time']]; const body = rows.map((r, i) => [ i + 1, r.roll_no || '', r.applicant_name || '', r.college_name || '', r.course_name || '', r.category || '', r.vacant_seat_rank || '', r.time_of_reporting || '' ]); doc.autoTable({ startY: 60, head, body, styles: { fontSize: 9 }, headStyles: { fillColor: [37, 99, 235], textColor: [255, 255, 255], fontStyle: 'bold', halign: 'center' } }); const filename = roundId ? `student_list_round_${roundId}.pdf` : 'student_admission_list.pdf'; doc.save(filename); } catch (e) { alert('Unable to generate Student List PDF: ' + (e.message || 'unknown error')); } } function normalizeMetricName(s) { const t = String(s || '').trim().toUpperCase().replace(/\s+/g, '_'); // Handle various forms of H Rank (including lowercase hrank from database) if ((t.includes('H') && t.includes('RANK')) || t === 'H_RANK' || t === 'HRANK' || t === 'H-RANK' || t === 'HRANK') { return 'H_RANK'; } // Handle various forms of Merit Marks (including lowercase merit_marks from database) if ((t.includes('MERIT') && t.includes('MARK')) || t === 'MERIT_MARKS' || t === 'MERITMARKS' || t === 'MERIT-MARKS' || t === 'MERIT_MARK') { return 'MERIT_MARKS'; } return t; } function pivotCutoffMetrics(rows) { const list = Array.isArray(rows) ? rows : []; const preferredCatOrder = ['OPEN','SC','ST','SEBC','EWS','ESM','TFWS']; const examsSet = new Set(); const catsSet = new Set(); const byKey = new Map(); const metricNamesSeen = new Set(); // Debug: track metric names list.forEach(r => { const exam = (r.exam_name || '').toString(); const cat = (r.category_code || '').toString().toUpperCase(); const originalMetric = (r.metric || '').toString(); const metric = normalizeMetricName(originalMetric); const value = r.value; if (!exam || !cat) return; metricNamesSeen.add(originalMetric); // Debug: track original metric names examsSet.add(exam); catsSet.add(cat); const key = (r.college_name || '') + '||' + (r.course_name || ''); if (!byKey.has(key)) byKey.set(key, { college_name: r.college_name || '', course_name: r.course_name || '', values: {} }); const row = byKey.get(key); if (!row.values[exam]) row.values[exam] = {}; if (!row.values[exam][cat]) row.values[exam][cat] = {}; // Store value, handling null/undefined/empty string (but allow 0 as valid) if (value != null && value !== '' && value !== 'null' && value !== 'NULL') { row.values[exam][cat][metric] = value; // Also store with original metric name as key (in case normalization doesn't match) row.values[exam][cat][originalMetric] = value; } // Also store the original metric name for debugging if (!row.values[exam][cat]._originalMetrics) { row.values[exam][cat]._originalMetrics = {}; } row.values[exam][cat]._originalMetrics[originalMetric] = value; }); // Debug: log metric names found if (metricNamesSeen.size > 0) { console.log('Metric names found in data:', Array.from(metricNamesSeen)); console.log('Total rows processed:', list.length); console.log('Unique college-course combinations:', byKey.size); // Show sample of what was stored if (byKey.size > 0) { const firstKey = Array.from(byKey.keys())[0]; const firstRow = byKey.get(firstKey); console.log('Sample stored structure:', { college: firstRow.college_name, course: firstRow.course_name, values: firstRow.values }); } } const exams = Array.from(examsSet); // Order exams: GUJCET first, JEE next, then others exams.sort((a,b) => { const rank = (x) => x.toUpperCase() === 'GUJCET' ? 0 : (x.toUpperCase() === 'JEE' ? 1 : 2); const ra = rank(a), rb = rank(b); return ra === rb ? a.localeCompare(b) : ra - rb; }); let categories = Array.from(catsSet); categories.sort((a,b) => { const ia = preferredCatOrder.indexOf(a); const ib = preferredCatOrder.indexOf(b); if (ia === -1 && ib === -1) return a.localeCompare(b); if (ia === -1) return 1; if (ib === -1) return -1; return ia - ib; }); const rowsOut = Array.from(byKey.values()).sort((a,b) => { const ca = (a.college_name || '').localeCompare(b.college_name || ''); if (ca !== 0) return ca; return (a.course_name || '').localeCompare(b.course_name || ''); }); return { exams, categories, rows: rowsOut }; } function downloadMetricsPivotPdf(pivot, year) { const { jsPDF } = window.jspdf; const doc = new jsPDF({ orientation:'landscape', unit:'pt', format:'a3' }); doc.setTextColor(34, 34, 34); doc.setFontSize(16); doc.text(`Cutoff Metrics ${year}`, 40, 40); const exams = pivot.exams || []; const cats = pivot.categories || []; // Build multi-row head with colSpans const headRow1 = [ { content:'College', rowSpan:3 }, { content:'Course', rowSpan:3 } ]; exams.forEach(ex => headRow1.push({ content: ex, colSpan: cats.length * 2 })); const headRow2 = []; exams.forEach(ex => cats.forEach(cat => headRow2.push({ content: cat, colSpan: 2 }))); const headRow3 = []; exams.forEach(ex => cats.forEach(cat => { headRow3.push('H Rank'); headRow3.push('Merit Marks'); })); // Body rows const body = (pivot.rows || []).map(r => { const arr = [r.college_name || '', r.course_name || '']; exams.forEach(ex => cats.forEach(cat => { const v = (r.values && r.values[ex] && r.values[ex][cat]) || {}; arr.push(v && v.H_RANK != null && v.H_RANK !== '' ? String(v.H_RANK) : 'β€”'); arr.push(v && v.MERIT_MARKS != null && v.MERIT_MARKS !== '' ? String(v.MERIT_MARKS) : 'β€”'); })); return arr; }); doc.autoTable({ startY: 60, head: [headRow1, headRow2, headRow3], body, styles:{ fontSize:8, cellPadding:3 }, headStyles:{ fillColor:[240,240,240] }, columnStyles:{ 0:{ cellWidth:200 }, 1:{ cellWidth:160 } } }); doc.save(`cutoff_metrics_pivot_${year}.pdf`); } function App() { const route = useHashRoute(); const routePath = (route.split('?')[0] || '#/'); const params = parseHashParams(route); const [catalog, setCatalog] = React.useState(defaultCatalog); // Make nav sticky below header React.useEffect(() => { const updateNavPosition = () => { const header = document.querySelector('.site-header'); const nav = document.querySelector('.primary-nav'); if (header && nav) { // Calculate header height and position nav below it const headerHeight = header.offsetHeight; nav.style.top = headerHeight + 'px'; } }; updateNavPosition(); window.addEventListener('resize', updateNavPosition); // Also update after a short delay to ensure header is rendered setTimeout(updateNavPosition, 100); return () => { window.removeEventListener('resize', updateNavPosition); }; }, []); const [collegesByField, setCollegesByField] = React.useState({ Engineering: ['LD College of Engineering', 'VGEC', 'Nirma University', 'DAIICT'], Pharmacy: ['L.M. College of Pharmacy', 'Nirma Institute of Pharmacy'], Architecture: ['CEPT University'], Management: ['BK School of Management', 'Nirma Institute of Management'], IT: ['DAIICT', 'Nirma University'], Commerce: ['H.L. College of Commerce'], Arts: ["St. Xavier's College, Ahmedabad"], }); // Scroll to top button state const [showScrollTop, setShowScrollTop] = React.useState(false); React.useEffect(() => { fetch('api/catalog.php') .then(safeJsonParse) .then(setCatalog) .catch(() => setCatalog([])); fetch('api/colleges.php') .then(safeJsonParse) .then(setCollegesByField) .catch(() => setCollegesByField({})); }, []); // Handle scroll to show/hide scroll-to-top button React.useEffect(() => { const handleScroll = () => { setShowScrollTop(window.scrollY > 300); }; window.addEventListener('scroll', handleScroll); return () => window.removeEventListener('scroll', handleScroll); }, []); const scrollToTop = () => { window.scrollTo({ top: 0, behavior: 'smooth' }); }; const [showDisclaimer, setShowDisclaimer] = React.useState(() => { // Check if user has dismissed the disclaimer return localStorage.getItem('disclaimerDismissed') !== 'true'; }); const dismissDisclaimer = () => { setShowDisclaimer(false); localStorage.setItem('disclaimerDismissed', 'true'); }; // Visit counter state const [visitStats, setVisitStats] = React.useState({ total_visits: 0, today_visits: 0, unique_visitors: 0 }); // Track visit and load stats React.useEffect(() => { const trackVisit = async () => { try { // Track the visit const response = await fetch('api/track_visit.php?t=' + Date.now()); const data = await safeJsonParse(response); if (data && data.total_visits !== undefined) { setVisitStats({ total_visits: data.total_visits || 0, today_visits: data.today_visits || 0, unique_visitors: data.unique_visitors || 0 }); } } catch (error) { console.error('Failed to track visit:', error); // Try to get stats without tracking try { const statsResponse = await fetch('api/get_visit_stats.php?t=' + Date.now()); const statsData = await safeJsonParse(statsResponse); if (statsData && statsData.total_visits !== undefined) { setVisitStats({ total_visits: statsData.total_visits || 0, today_visits: statsData.today_visits || 0, unique_visitors: statsData.unique_visitors || 0 }); } } catch (statsError) { console.error('Failed to get visit stats:', statsError); } } }; trackVisit(); }, []); return (
{/* Disclaimer Banner */} {showDisclaimer && (
⚠️
Important Notice - Testing/Development Site

This is a testing/development website created for educational and demonstration purposes. This site is NOT the official ACPC website and is not affiliated with, endorsed by, or connected to MY ADMISSION (ACPC) or the Government of Gujarat. All data, information, and results displayed are for testing purposes only and should not be used for making actual admission decisions. Please refer to the official ACPC website ( gujacpc.admissions.nic.in ) for accurate and official admission information.

)}
Get My Admission (Unofficial)
Empowering Dreams, Shaping Futures
{/* Search removed - use Rank Finder page instead */}
{routePath === '#/' && ()} {routePath === '#/courses' && ( <>

Courses

Home / Courses
)} {routePath === '#/finder' && ()}
Β© {new Date().getFullYear()} MY ADMISSION
Disclaimer: This is a testing/development website for educational purposes only. This site is NOT the official ACPC website and is not affiliated with, endorsed by, or connected to MY ADMISSION (ACPC) or the Government of Gujarat. All information displayed is for testing purposes only and should not be used for making actual admission decisions. For official admission information, please visit the official ACPC website at{' '} gujacpc.admissions.nic.in .
Terms β€’ Privacy
{/* Scroll to Top Button */} {showScrollTop && ( )}
); } // Helper to build filename-safe slugs from college names function slugifyName(name) { return (name || '') .toLowerCase() .normalize('NFKD').replace(/[\u0300-\u036f]/g, '') .replace(/&/g, ' and ') .replace(/[^a-z0-9]+/g, '-') .replace(/^-+|-+$/g, '') .replace(/-+/g, '-'); } // Renders a college photo with fallbacks: {slug}{1..3}.{jpg|jpeg|png|webp} function CollegePhoto({ college, photo_url }) { const base = React.useMemo(() => slugifyName(college), [college]); const candidates = React.useMemo(() => { if (!base) return []; const seq = [1, 2, 3]; const exts = ['.jpg', '.jpeg', '.png', '.webp']; const arr = []; seq.forEach(n => exts.forEach(ext => arr.push(`/uploads/colleges/${base}${n}${ext}`))); return arr; }, [base]); const [src, setSrc] = React.useState(photo_url || (candidates[0] || '')); const tried = React.useRef(new Set()); React.useEffect(() => { tried.current = new Set(); setSrc(photo_url || (candidates[0] || '')); }, [photo_url, base]); const onError = () => { const idx = candidates.findIndex(u => u === src); for (let i = idx + 1; i < candidates.length; i++) { if (!tried.current.has(candidates[i])) { tried.current.add(candidates[i]); setSrc(candidates[i]); return; } } setSrc(''); }; if (!base) return 'β€”'; return src ? {college} : 'β€”'; } const root = ReactDOM.createRoot(document.getElementById('root')); root.render();