diff --git a/package.json b/package.json index 194ad79..027da30 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,10 @@ "react-router-dom": "^5.0.1", "react-scripts": "^5.0.0", "react-spinners": "^0.6.1", - "typescript": "^4.5.4" + "typescript": "^4.5.4", + "@algolia/autocomplete-js": "^1.17.0", + "@algolia/autocomplete-theme-classic": "^1.17.0", + "algoliasearch": "^4.24.0" }, "scripts": { "start": "react-scripts start", @@ -52,4 +55,4 @@ "enzyme": "^3.10.0", "enzyme-adapter-react-16": "^1.14.0" } -} +} \ No newline at end of file diff --git a/src/algoliaConfig.js b/src/algoliaConfig.js new file mode 100644 index 0000000..4dacca4 --- /dev/null +++ b/src/algoliaConfig.js @@ -0,0 +1,7 @@ +// Algolia configuration for Atila Scholarships +// These are placeholder values - replace with actual Algolia credentials +export const algoliaConfig = { + appId: 'YOUR_ALGOLIA_APP_ID', + apiKey: 'YOUR_ALGOLIA_SEARCH_API_KEY', + indexName: 'atila_scholarships', +}; diff --git a/src/components/ScholarshipSearch.js b/src/components/ScholarshipSearch.js new file mode 100644 index 0000000..f49de64 --- /dev/null +++ b/src/components/ScholarshipSearch.js @@ -0,0 +1,84 @@ +import React, { useEffect, useRef } from 'react'; +import { autocomplete, getAlgoliaResults } from '@algolia/autocomplete-js'; +import '@algolia/autocomplete-theme-classic/dist/theme.css'; +import { algoliaConfig } from '../algoliaConfig'; +import algoliasearch from 'algoliasearch'; + +const searchClient = algoliasearch(algoliaConfig.appId, algoliaConfig.apiKey); + +function ScholarshipSearch({ onSearch, initialValue = '' }) { + const containerRef = useRef(null); + + useEffect(() => { + if (!containerRef.current) return; + + const search = autocomplete({ + container: containerRef.current, + placeholder: 'Search scholarships...', + initialState: { query: initialValue }, + openOnFocus: true, + getSources({ query }) { + return [ + { + sourceId: 'scholarships', + getItems() { + return getAlgoliaResults({ + searchClient, + queries: [ + { + indexName: algoliaConfig.indexName, + query, + params: { + hitsPerPage: 5, + }, + }, + ], + }); + }, + getItemInputValue({ item }) { + return item.name || item.title || ''; + }, + onSelect({ item }) { + if (onSearch) { + onSearch(item.name || item.title || ''); + } + }, + templates: { + item({ item, components }) { + return ( +
+
+
+
+ +
+ {item.deadline && ( +
+ Deadline: {item.deadline} +
+ )} + {item.amount && ( +
+ Amount: {item.amount} +
+ )} +
+
+
+ ); + }, + }, + }, + ]; + }, + }); + + return () => { + search.destroy(); + }; + }, [initialValue, onSearch]); + + return
; +} + +export default ScholarshipSearch; diff --git a/src/scenes/Scholarship/ScholarshipsList/ScholarshipsList.js b/src/scenes/Scholarship/ScholarshipsList/ScholarshipsList.js index 9fe501b..d11e74f 100644 --- a/src/scenes/Scholarship/ScholarshipsList/ScholarshipsList.js +++ b/src/scenes/Scholarship/ScholarshipsList/ScholarshipsList.js @@ -4,6 +4,7 @@ import BarLoader from 'react-spinners/BarLoader'; import {toTitleCase} from "../../../services/utils"; import ScholarshipCard from "../ScholarshipCard"; import ScholarshipsAPI from "../../../services/ScholarshipsAPI"; +import ScholarshipSearch from "../../../components/ScholarshipSearch"; class ScholarshipsList extends React.Component { constructor(props) { @@ -91,6 +92,14 @@ class ScholarshipsList extends React.Component { return (
+
+ { + this.props.history.push(`/scholarship/s/${query}`); + }} + initialValue={searchQuery || ''} + /> +

{searchQuery && `Scholarships for ${toTitleCase(searchQuery)}`} {!searchQuery && 'Scholarships'}