From 0495e8c0cda806b784b1885e311e7fe30c504668 Mon Sep 17 00:00:00 2001 From: Justus Kluge Date: Wed, 7 May 2025 14:57:39 +0200 Subject: [PATCH 1/2] firebase hosting updated --- .github/workflows/firebase-hosting-merge.yml | 20 ++++++++++++++++++ .../firebase-hosting-pull-request.yml | 21 +++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 .github/workflows/firebase-hosting-merge.yml create mode 100644 .github/workflows/firebase-hosting-pull-request.yml diff --git a/.github/workflows/firebase-hosting-merge.yml b/.github/workflows/firebase-hosting-merge.yml new file mode 100644 index 00000000..68c33d74 --- /dev/null +++ b/.github/workflows/firebase-hosting-merge.yml @@ -0,0 +1,20 @@ +# This file was auto-generated by the Firebase CLI +# https://github.com/firebase/firebase-tools + +name: Deploy to Firebase Hosting on merge +on: + push: + branches: + - main +jobs: + build_and_deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: npm ci && npm run build + - uses: FirebaseExtended/action-hosting-deploy@v0 + with: + repoToken: ${{ secrets.GITHUB_TOKEN }} + firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_FINDMYNEXTCOURSE }} + channelId: live + projectId: findmynextcourse diff --git a/.github/workflows/firebase-hosting-pull-request.yml b/.github/workflows/firebase-hosting-pull-request.yml new file mode 100644 index 00000000..09f4233d --- /dev/null +++ b/.github/workflows/firebase-hosting-pull-request.yml @@ -0,0 +1,21 @@ +# This file was auto-generated by the Firebase CLI +# https://github.com/firebase/firebase-tools + +name: Deploy to Firebase Hosting on PR +on: pull_request +permissions: + checks: write + contents: read + pull-requests: write +jobs: + build_and_preview: + if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: npm ci && npm run build + - uses: FirebaseExtended/action-hosting-deploy@v0 + with: + repoToken: ${{ secrets.GITHUB_TOKEN }} + firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_FINDMYNEXTCOURSE }} + projectId: findmynextcourse From 5db3cf1dd7794213016a991571a369e8fccd90d0 Mon Sep 17 00:00:00 2001 From: Justus Kluge Date: Wed, 7 May 2025 15:31:37 +0200 Subject: [PATCH 2/2] Hopefully better search function --- my-app/package-lock.json | 92 ++++++++++++++------ my-app/package.json | 2 + my-app/src/presenters/SearchbarPresenter.jsx | 39 ++++++--- 3 files changed, 90 insertions(+), 43 deletions(-) diff --git a/my-app/package-lock.json b/my-app/package-lock.json index 2f16fd62..43ca33fe 100644 --- a/my-app/package-lock.json +++ b/my-app/package-lock.json @@ -15,7 +15,9 @@ "@xyflow/react": "^12.5.5", "autoprefixer": "^10.4.21", "firebase": "^11.5.0", + "fuse.js": "^7.1.0", "ldrs": "^1.1.6", + "lodash.debounce": "^4.0.8", "lodash.throttle": "^4.1.1", "mobx": "^6.13.7", "mobx-react-lite": "^4.1.0", @@ -177,9 +179,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", - "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", "dev": true, "license": "MIT", "engines": { @@ -354,6 +356,16 @@ "node": ">17.0.0" } }, + "node_modules/@emnapi/runtime": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", + "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.2", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", @@ -2797,12 +2809,6 @@ "@babel/types": "^7.20.7" } }, - "node_modules/@types/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", - "license": "MIT" - }, "node_modules/@types/d3": { "version": "7.4.3", "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", @@ -3893,9 +3899,9 @@ } }, "node_modules/fdir": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", - "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", "license": "MIT", "peerDependencies": { "picomatch": "^3 || ^4" @@ -4021,6 +4027,15 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/fuse.js": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.1.0.tgz", + "integrity": "sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=10" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -4525,6 +4540,12 @@ "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", "license": "MIT" }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -4903,12 +4924,11 @@ } }, "node_modules/react-router": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.5.0.tgz", - "integrity": "sha512-estOHrRlDMKdlQa6Mj32gIks4J+AxNsYoE0DbTTxiMy2mPzZuWSDU+N85/r1IlNR7kGfznF3VCUlvc5IUO+B9g==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.5.3.tgz", + "integrity": "sha512-3iUDM4/fZCQ89SXlDa+Ph3MevBrozBAI655OAfWQlTm9nBR0IKlrmNwFow5lPHttbwvITZfkeeeZFP6zt3F7pw==", "license": "MIT", "dependencies": { - "@types/cookie": "^0.6.0", "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0", "turbo-stream": "2.4.0" @@ -4927,12 +4947,12 @@ } }, "node_modules/react-router-dom": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.5.0.tgz", - "integrity": "sha512-fFhGFCULy4vIseTtH5PNcY/VvDJK5gvOWcwJVHQp8JQcWVr85ENhJ3UpuF/zP1tQOIFYNRJHzXtyhU1Bdgw0RA==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.5.3.tgz", + "integrity": "sha512-cK0jSaTyW4jV9SRKAItMIQfWZ/D6WEZafgHuuCb9g+SjhLolY78qc+De4w/Cz9ybjvLzShAmaIMEXt8iF1Cm+A==", "license": "MIT", "dependencies": { - "react-router": "7.5.0" + "react-router": "7.5.3" }, "engines": { "node": ">=20.0.0" @@ -5178,12 +5198,12 @@ } }, "node_modules/tinyglobby": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", - "integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==", + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", + "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", "license": "MIT", "dependencies": { - "fdir": "^6.4.3", + "fdir": "^6.4.4", "picomatch": "^4.0.2" }, "engines": { @@ -5275,17 +5295,17 @@ } }, "node_modules/vite": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.1.tgz", - "integrity": "sha512-kkzzkqtMESYklo96HKKPE5KKLkC1amlsqt+RjFMlX2AvbRB/0wghap19NdBxxwGZ+h/C6DLCrcEphPIItlGrRQ==", + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", + "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", "license": "MIT", "dependencies": { "esbuild": "^0.25.0", - "fdir": "^6.4.3", + "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", - "tinyglobby": "^0.2.12" + "tinyglobby": "^0.2.13" }, "bin": { "vite": "bin/vite.js" @@ -5436,6 +5456,20 @@ "dev": true, "license": "ISC" }, + "node_modules/yaml": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", + "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", + "license": "ISC", + "optional": true, + "peer": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", diff --git a/my-app/package.json b/my-app/package.json index 2c237a57..05512cb0 100644 --- a/my-app/package.json +++ b/my-app/package.json @@ -17,7 +17,9 @@ "@xyflow/react": "^12.5.5", "autoprefixer": "^10.4.21", "firebase": "^11.5.0", + "fuse.js": "^7.1.0", "ldrs": "^1.1.6", + "lodash.debounce": "^4.0.8", "lodash.throttle": "^4.1.1", "mobx": "^6.13.7", "mobx-react-lite": "^4.1.0", diff --git a/my-app/src/presenters/SearchbarPresenter.jsx b/my-app/src/presenters/SearchbarPresenter.jsx index 1f625be2..81716647 100644 --- a/my-app/src/presenters/SearchbarPresenter.jsx +++ b/my-app/src/presenters/SearchbarPresenter.jsx @@ -1,27 +1,39 @@ -import React, { useEffect } from 'react'; +import React, { useEffect, useCallback } from 'react'; import { observer } from "mobx-react-lite"; import { useState } from 'react'; import CoursePagePopup from '../views/Components/CoursePagePopup.jsx'; import PrerequisitePresenter from './PrerequisitePresenter.jsx'; import { ReviewPresenter } from "../presenters/ReviewPresenter.jsx"; import SearchbarView from "../views/SearchbarView.jsx"; +import Fuse from 'fuse.js' +import debounce from 'lodash.debounce'; const SearchbarPresenter = observer(({ model }) => { - const searchCourses = (query) => { - //model.filteredCourses is essentially a smaller subset of model.courses, if theres no filters, it should be the same - console.log("---------------search recalculated"); - console.log("filtered courses length: ", model.filteredCourses.length); - const searchResults = model.filteredCourses.filter(course => - course.code.toLowerCase().includes(query.toLowerCase()) || - course.name.toLowerCase().includes(query.toLowerCase()) || - course.description?.toLowerCase().includes(query.toLowerCase()) - ); - model.setCurrentSearchText(query); - model.setCurrentSearch(searchResults); - console.log(model.currentSearch.length); + const [searchQuery, setSearchQuery] = useState(""); + + const fuseOptions = { + keys: [ + { name: 'code', weight: 0.6 }, + { name: 'name', weight: 0.3 }, + { name: 'description', weight: 0.1 }, + ], + threshold: 0.3, // adjust this for sensitivity + ignoreLocation: true, + minMatchCharLength: 2, }; + // Debounced search function + const searchCourses = useCallback(debounce((query) => { + if (!query.trim()) { + model.setCurrentSearch(model.filteredCourses); + } else { + const fuse = new Fuse(model.filteredCourses, fuseOptions); + const results = fuse.search(query).map((r) => r.item); + model.setCurrentSearch(results); + } + }, 500), []); + const addFavourite = (course) => { model.addFavourite(course); }; @@ -54,7 +66,6 @@ const SearchbarPresenter = observer(({ model }) => { const [selectedCourse, setSelectedCourse] = useState(null); const preP = ; const reviewPresenter = ; - const [searchQuery, setSearchQuery] = useState(""); const popup =