diff --git a/client/package-lock.json b/client/package-lock.json index 843edde..08bcf65 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -14,11 +14,13 @@ "@testing-library/react": "^16.3.0", "@testing-library/user-event": "^13.5.0", "axios": "^1.11.0", + "framer-motion": "^12.23.26", "jwt-decode": "^4.0.0", "lucide-react": "^0.526.0", "react": "^19.1.0", "react-dom": "^19.1.0", "react-icons": "^5.5.0", + "react-intersection-observer": "^10.0.0", "react-router-dom": "^7.6.3", "react-scripts": "^5.0.1", "web-vitals": "^2.1.4" @@ -8502,6 +8504,33 @@ "url": "https://github.com/sponsors/rawify" } }, + "node_modules/framer-motion": { + "version": "12.23.26", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.26.tgz", + "integrity": "sha512-cPcIhgR42xBn1Uj+PzOyheMtZ73H927+uWPDVhUMqxy8UHt6Okavb6xIz9J/phFUHUj0OncR6UvMfJTXoc/LKA==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.23.23", + "motion-utils": "^12.23.6", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -11636,6 +11665,21 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/motion-dom": { + "version": "12.23.23", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.23.tgz", + "integrity": "sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.23.6" + } + }, + "node_modules/motion-utils": { + "version": "12.23.6", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz", + "integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==", + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -14084,6 +14128,21 @@ "react": "*" } }, + "node_modules/react-intersection-observer": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-10.0.0.tgz", + "integrity": "sha512-JJRgcnFQoVXmbE5+GXr1OS1NDD1gHk0HyfpLcRf0575IbJz+io8yzs4mWVlfaqOQq1FiVjLvuYAdEEcrrCfveg==", + "license": "MIT", + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", diff --git a/client/package.json b/client/package.json index 2c5d2ab..4a759d0 100644 --- a/client/package.json +++ b/client/package.json @@ -9,11 +9,13 @@ "@testing-library/react": "^16.3.0", "@testing-library/user-event": "^13.5.0", "axios": "^1.11.0", + "framer-motion": "^12.23.26", "jwt-decode": "^4.0.0", "lucide-react": "^0.526.0", "react": "^19.1.0", "react-dom": "^19.1.0", "react-icons": "^5.5.0", + "react-intersection-observer": "^10.0.0", "react-router-dom": "^7.6.3", "react-scripts": "^5.0.1", "web-vitals": "^2.1.4" diff --git a/client/src/App.js b/client/src/App.js index dbdfff2..7fb551e 100644 --- a/client/src/App.js +++ b/client/src/App.js @@ -1,6 +1,7 @@ import React, { useState, useEffect } from "react"; import "./components/styles.css"; import { Routes, Route } from "react-router-dom"; +import { AnimatePresence } from "framer-motion"; import HomePage from "./pages/HomePage"; import PharmacyPage from "./pages/PharmacyPage"; import LabsPage from "./pages/LabsPage"; @@ -13,14 +14,53 @@ import EmergencyPanel from "./components/EmergencyPanel"; import ForgotPassword from "./pages/ForgotPassword"; import ProtectedRoute from "./components/ProtectedRoute"; import AdminDashboardPage from "./pages/AdminDashboardPage"; -import MedicineListing from "./pages/Medicine"; // <-- import the medicine page +import MedicineListing from "./pages/Medicine"; +import CustomCursor from "./components/CustomCursor"; + +// Check if device supports hover (non-touch devices) +const useIsTouchDevice = () => { + const [isTouchDevice, setIsTouchDevice] = useState(false); + + useEffect(() => { + const checkTouchDevice = () => { + setIsTouchDevice( + 'ontouchstart' in window || + navigator.maxTouchPoints > 0 || + navigator.msMaxTouchPoints > 0 + ); + }; + + checkTouchDevice(); + window.addEventListener('resize', checkTouchDevice); + return () => window.removeEventListener('resize', checkTouchDevice); + }, []); + + return isTouchDevice; +}; function App() { const [darkMode, setDarkMode] = useState(false); const [isEmergencyOpen, setIsEmergencyOpen] = useState(false); + const isTouchDevice = useIsTouchDevice(); + const location = window.location; useEffect(() => { document.body.classList.toggle("dark-mode", darkMode); + + // Add smooth scrolling for anchor links + const smoothScroll = (e) => { + const targetId = e.target.getAttribute('href'); + if (targetId && targetId.startsWith('#')) { + e.preventDefault(); + const targetElement = document.querySelector(targetId); + if (targetElement) { + targetElement.scrollIntoView({ behavior: 'smooth' }); + } + } + }; + + document.addEventListener('click', smoothScroll); + return () => document.removeEventListener('click', smoothScroll); }, [darkMode]); const toggleEmergencyPanel = () => { @@ -29,26 +69,29 @@ function App() { return ( <> - - } /> - } /> - } /> - } /> - } /> {/* new route */} - } /> - } /> - } /> - } /> - - - - } - /> - } /> - + {!isTouchDevice && } + + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + + + } + /> + } /> + + {/* Fixed Emergency Button */}