Implemented sign in & sign up (with token storage)

This commit is contained in:
vedang29
2025-04-18 16:33:40 +05:30
parent 5391410609
commit 76f9b00624
6 changed files with 487 additions and 260 deletions
+1
View File
@@ -0,0 +1 @@
VITE_API_URL=http://192.168.29.61:8080
+30 -4
View File
@@ -1,11 +1,11 @@
{ {
"name": "drive-thru", "name": "Skycrate",
"version": "0.0.0", "version": "0.0.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "drive-thru", "name": "Skycrate",
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "dependencies": {
"@reduxjs/toolkit": "^2.6.0", "@reduxjs/toolkit": "^2.6.0",
@@ -13,6 +13,7 @@
"lucide-react": "^0.476.0", "lucide-react": "^0.476.0",
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
"react-hot-toast": "^2.5.2",
"react-icons": "^5.5.0", "react-icons": "^5.5.0",
"react-redux": "^9.2.0", "react-redux": "^9.2.0",
"react-router-dom": "^7.2.0" "react-router-dom": "^7.2.0"
@@ -1929,8 +1930,7 @@
"node_modules/csstype": { "node_modules/csstype": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
"devOptional": true
}, },
"node_modules/data-view-buffer": { "node_modules/data-view-buffer": {
"version": "1.0.2", "version": "1.0.2",
@@ -2767,6 +2767,15 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/goober": {
"version": "2.1.16",
"resolved": "https://registry.npmjs.org/goober/-/goober-2.1.16.tgz",
"integrity": "sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==",
"license": "MIT",
"peerDependencies": {
"csstype": "^3.0.10"
}
},
"node_modules/gopd": { "node_modules/gopd": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
@@ -4036,6 +4045,23 @@
"react": "^19.0.0" "react": "^19.0.0"
} }
}, },
"node_modules/react-hot-toast": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.5.2.tgz",
"integrity": "sha512-Tun3BbCxzmXXM7C+NI4qiv6lT0uwGh4oAfeJyNOjYUejTsm35mK9iCaYLGv8cBz9L5YxZLx/2ii7zsIwPtPUdw==",
"license": "MIT",
"dependencies": {
"csstype": "^3.1.3",
"goober": "^2.1.16"
},
"engines": {
"node": ">=10"
},
"peerDependencies": {
"react": ">=16",
"react-dom": ">=16"
}
},
"node_modules/react-icons": { "node_modules/react-icons": {
"version": "5.5.0", "version": "5.5.0",
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz", "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz",
+1
View File
@@ -15,6 +15,7 @@
"lucide-react": "^0.476.0", "lucide-react": "^0.476.0",
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
"react-hot-toast": "^2.5.2",
"react-icons": "^5.5.0", "react-icons": "^5.5.0",
"react-redux": "^9.2.0", "react-redux": "^9.2.0",
"react-router-dom": "^7.2.0" "react-router-dom": "^7.2.0"
+158 -208
View File
@@ -1,144 +1,171 @@
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { toast } from 'react-hot-toast';
import { useNavigate } from 'react-router-dom';
const Sidebar = () => { const Sidebar = () => {
const navigate = useNavigate(); // Hook for programmatic navigation
const handleLogout = () => {
// Show loading toast
const loadingToast = toast.loading("Logging out...");
// Simulate a delay (for example, network request)
setTimeout(() => {
// Remove the token from localStorage
localStorage.removeItem('token'); // Adjust the key if necessary
// Redirect user to the homepage
navigate('/');
// Show success toast after logout
toast.update(loadingToast, {
render: "Logged out successfully!",
type: "success",
isLoading: false,
autoClose: 2000,
});
}, 1500); // Simulate a 1.5 second delay before showing success
};
return ( return (
<> <>
<nav className="fixed top-0 z-50 h-[60px] w-full bg-white border-b border-gray-200 "> <nav className="fixed top-0 z-50 h-[60px] w-full bg-white border-b border-gray-200">
<div className="p-[15px] h-full lg:px-5 lg:pl-3 "> <div className="p-[15px] h-full lg:px-5 lg:pl-3">
<div className="flex h-full items-center justify-between"> <div className="flex h-full items-center justify-between">
<div className="flex items-center justify-start rtl:justify-end"> {/* Left Section - Logo */}
<button <div className="flex items-center justify-start rtl:justify-end">
data-drawer-target="logo-sidebar" <button
data-drawer-toggle="logo-sidebar" data-drawer-target="logo-sidebar"
aria-controls="logo-sidebar" data-drawer-toggle="logo-sidebar"
type="button" aria-controls="logo-sidebar"
className="inline-flex items-center p-2 text-lg text-white rounded-lg sm:hidden hover:bg-[#37A0EA] focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-[#37A0EA] dark:focus:ring-gray-600" type="button"
className="inline-flex items-center p-2 text-lg text-white rounded-lg sm:hidden hover:bg-[#37A0EA] focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-[#37A0EA] dark:focus:ring-gray-600"
>
<span className="sr-only">Open sidebar</span>
<svg
className="w-6 h-6"
aria-hidden="true"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
> >
<span className="sr-only">Open sidebar</span> <path
<svg clipRule="evenodd"
className="w-6 h-6" fillRule="evenodd"
aria-hidden="true" d="M2 4.75A.75.75 0 012.75 4h14.5a.75.75 0 010 1.5H2.75A.75.75 0 012 4.75zm0 10.5a.75.75 0 01.75-.75h7.5a.75.75 0 010 1.5h-7.5a.75.75 0 01-.75-.75zM2 10a.75.75 0 01.75-.75h14.5a.75.75 0 010 1.5H2.75A.75.75 0 012 10z"
fill="currentColor" ></path>
viewBox="0 0 20 20" </svg>
xmlns="http://www.w3.org/2000/svg" </button>
> <Link to="/" className="flex ms-2 md:me-24">
<path <img
clipRule="evenodd" src="./image.png"
fillRule="evenodd" className="h-8 me-3"
d="M2 4.75A.75.75 0 012.75 4h14.5a.75.75 0 010 1.5H2.75A.75.75 0 012 4.75zm0 10.5a.75.75 0 01.75-.75h7.5a.75.75 0 010 1.5h-7.5a.75.75 0 01-.75-.75zM2 10a.75.75 0 01.75-.75h14.5a.75.75 0 010 1.5H2.75A.75.75 0 012 10z" alt="Skycrate Logo"
></path> />
</svg> <span className="self-center text-xl font-semibold sm:text-2xl whitespace-nowrap">
Skycrate
</span>
</Link>
</div>
{/* Right Section - Search & User Menu */}
<div className="flex items-center">
{/* Search Bar */}
<div className="flex items-center justify-end mr-40">
<input
type="text"
placeholder="Search..."
className="w-full border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
<button
type="button"
className="ml-2 px-4 py-2 text-white bg-blue-500 rounded-lg hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500"
>
Search
</button> </button>
<Link to="/" className="flex ms-2 md:me-24">
<img
src="./image.png"
className="h-8 me-3"
alt="Skycrate Logo"
/>
<span className="self-center text-xl font-semibold sm:text-2xl whitespace-nowrap ">
Skycrate
</span>
</Link>
</div> </div>
<div className="flex items-center"> {/* User Profile & Dropdown */}
{" "} <div className="flex items-center ms-3">
<div className="flex items-center justify-end mr-40 "> <div>
<input
type="text"
placeholder="Search..."
className="w-full border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
<button <button
type="button" type="button"
className="ml-2 px-4 py-2 text-white bg-blue-500 rounded-lg hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500" className="flex text-lg bg-gray-800 rounded-full focus:ring-4 focus:ring-gray-300 dark:focus:ring-gray-600"
aria-expanded="false"
data-dropdown-toggle="dropdown-user"
> >
Search <span className="sr-only">Open user menu</span>
<img
className="w-8 h-8 rounded-full"
src="https://flowbite.com/docs/images/people/profile-picture-5.jpg"
alt="User Photo"
/>
</button> </button>
</div> </div>
<div className="flex items-center ms-3"> <div
<div> className="z-50 hidden my-4 text-base list-none bg-[#1877F2] divide-y divide-gray-100 rounded-sm shadow-sm dark:bg-gray-700 dark:divide-gray-600"
<button id="dropdown-user"
type="button" >
className="flex text-lg bg-gray-800 rounded-full focus:ring-4 focus:ring-gray-300 dark:focus:ring-gray-600" <div className="px-4 py-3" role="none">
aria-expanded="false" <p className="text-lg text-white dark:text-white" role="none">
data-dropdown-toggle="dropdown-user" Neil Sims
</p>
<p
className="text-lg font-medium text-white truncate dark:text-gray-300"
role="none"
> >
<span className="sr-only">Open user menu</span> Skycrate@Skycrate.com
<img </p>
className="w-8 h-8 rounded-full"
src="https://flowbite.com/docs/images/people/profile-picture-5.jpg"
alt="user photo"
/>
</button>
</div> </div>
<div <ul className="py-1" role="none">
className="z-50 hidden my-4 text-base list-none bg-[#1877F2] divide-y divide-gray-100 rounded-sm shadow-sm dark:bg-gray-700 dark:divide-gray-600" <li>
id="dropdown-user" <Link
> to="#"
<div className="px-4 py-3" role="none"> className="block px-4 py-2 text-lg text-white hover:bg-[#37A0EA] dark:text-gray-300 dark:hover:bg-[#37A0EA] dark:hover:text-white"
<p role="menuitem"
className="text-lg text-white dark:text-white"
role="none"
> >
Neil Sims Dashboard
</p> </Link>
<p </li>
className="text-lg font-medium text-white truncate dark:text-gray-300" <li>
role="none" <Link
to="#"
className="block px-4 py-2 text-lg text-white hover:bg-[#37A0EA] dark:text-gray-300 dark:hover:bg-[#37A0EA] dark:hover:text-white"
role="menuitem"
> >
Skycrate@Skycrate.com Settings
</p> </Link>
</div> </li>
<ul className="py-1" role="none"> <li>
<li> <Link
<Link to="#"
to="#" className="block px-4 py-2 text-lg text-white hover:bg-[#37A0EA] dark:text-gray-300 dark:hover:bg-[#37A0EA] dark:hover:text-white"
className="block px-4 py-2 text-lg text-white hover:bg-[#37A0EA] dark:text-gray-300 dark:hover:bg-[#37A0EA] dark:hover:text-white" role="menuitem"
role="menuitem" >
> Earnings
Dashboard </Link>
</Link> </li>
</li> <li>
{/* Log Out Button */}
<li> <button
<Link type="button"
to="#" onClick={handleLogout} // Trigger the logout function
className="block px-4 py-2 text-lg text-white hover:bg-[#37A0EA] dark:text-gray-300 dark:hover:bg-[##37A0EA] dark:hover:text-white" className="block px-4 py-2 text-lg text-white hover:bg-[#37A0EA] dark:text-gray-300 dark:hover:bg-[#37A0EA] dark:hover:text-white"
role="menuitem" role="menuitem"
> >
Settings Log out
</Link> </button>
</li> </li>
<li> </ul>
<Link
to="#"
className="block px-4 py-2 text-lg text-white hover:bg-[#37A0EA] dark:text-gray-300 dark:hover:bg-[#37A0EA] dark:hover:text-white"
role="menuitem"
>
Earnings
</Link>
</li>
<li>
<Link
to="#"
className="block px-4 py-2 text-lg text-white hover:bg-[#37A0EA] dark:text-gray-300 dark:hover:bg-[#37A0EA] dark:hover:text-white"
role="menuitem"
>
Sign out
</Link>
</li>
</ul>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</nav> </div>
</nav>
<aside <aside
id="logo-sidebar" id="logo-sidebar"
className="fixed top-0 left-0 z-40 w-64 h-screen pt-[60px] transition-transform -translate-x-full bg-[##1877F2] border-r border-gray-200 sm:translate-x-0 dark:bg-[#1877F2] dark:border-gray-700" className="fixed top-0 left-0 z-40 w-64 h-screen pt-[60px] transition-transform -translate-x-full bg-[#1877F2] border-r border-gray-200 sm:translate-x-0 dark:bg-[#1877F2] dark:border-gray-700"
aria-label="Sidebar" aria-label="Sidebar"
> >
<div className="h-full px-3 pb-4 overflow-y-auto bg-[#1877F2] dark:bg-[#1877F2] custom-scrollbar"> <div className="h-full px-3 pb-4 overflow-y-auto bg-[#1877F2] dark:bg-[#1877F2] custom-scrollbar">
@@ -173,14 +200,12 @@ const Sidebar = () => {
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
<path <path
d="M2.31763 16.0834C1.93846 16.0834 1.60669 15.9372 1.32231 15.6449C1.03794 15.3527 0.895752 15.0248 0.895752 14.6615V2.33856C0.895752 1.97519 1.03794 1.64737 1.32231 1.3551C1.60669 1.06282 1.93846 0.916687 2.31763 0.916687H8.97674L10.3986 2.33856H18.4322C18.7956 2.33856 19.1234 2.4847 19.4157 2.77697C19.7079 3.06925 19.8541 3.39707 19.8541 3.76044V14.6615C19.8541 15.0248 19.7079 15.3527 19.4157 15.6449C19.1234 15.9372 18.7956 16.0834 18.4322 16.0834H2.31763ZM9.4033 13.0026H16.9866V12.5524C16.9866 11.8888 16.6509 11.3477 15.9795 10.9291C15.308 10.5104 14.3799 10.3011 13.195 10.3011C12.0101 10.3011 11.0819 10.5104 10.4105 10.9291C9.73903 11.3477 9.4033 11.8888 9.4033 12.5524V13.0026ZM13.195 8.87919C13.6689 8.87919 14.0757 8.70935 14.4154 8.36968C14.7551 8.03001 14.9249 7.6232 14.9249 7.14924C14.9249 6.67528 14.7551 6.26847 14.4154 5.9288C14.0757 5.58913 13.6689 5.41929 13.195 5.41929C12.721 5.41929 12.3142 5.58913 11.9745 5.9288C11.6349 6.26847 11.465 6.67528 11.465 7.14924C11.465 7.6232 11.6349 8.03001 11.9745 8.36968C12.3142 8.70935 12.721 8.87919 13.195 8.87919Z" d="M2.31763 16.0834C1.93846 16.0834 1.60669 15.9372 1.32231 15.6449C1.03794 15.3527 0.895752 15.0248 0.895752 14.6615V2.33856C0.895752 1.97519 1.03794 1.64737 1.32231 1.3551C1.60669 1.06282 1.93846 0.916687 2.31763 0.916687H8.97674L10.3986 2.33856H18.4322C18.7956 2.33856 19.1234 2.4847 19.4157 2.77697C19.7079 3.06925 19.8541 3.39707 19.8541 3.76044V14.6615C19.8541 15.0248 19.7079 15.3527 19.4157 15.6449C19.1234 15.9372 18.7956 16.0834 18.4322 16.0834H10.3986L8.97674 14.6615H2.31763Z"
fill="white" fill="currentColor"
/> />
</svg> </svg>
<span className="flex-1 ms-3 whitespace-nowrap"> <span className="ms-3">Documents</span>
Shared with me
</span>
</Link> </Link>
</li> </li>
<li> <li>
@@ -190,61 +215,17 @@ const Sidebar = () => {
> >
<svg <svg
className="w-5 h-5" className="w-5 h-5"
fill="currentColor" viewBox="0 0 18 18"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path d="M5 3h2v18H5V3zm6 6h2v12h-2V9zm6-4h2v16h-2V5z" />
</svg>
<span className="flex-1 ms-3 whitespace-nowrap">
Statistics
</span>
</Link>
</li>
<li>
<Link
to="#"
className="flex items-center p-2 pt-4 pb-4 text-white rounded-lg dark:text-white hover:bg-[#37A0EA] dark:hover:bg-[#37A0EA] group"
>
<svg
className="w-5 h-5 text-white dark:text-white"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
fill="currentColor"
viewBox="0 0 24 24"
>
<path
fillRule="evenodd"
d="M3 6a2 2 0 0 1 2-2h5.532a2 2 0 0 1 1.536.72l1.9 2.28H3V6Zm0 3v10a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V9H3Z"
clipRule="evenodd"
/>
</svg>
<span className="flex-1 ms-3 whitespace-nowrap">My files</span>
</Link>
</li>
<li>
<Link
to="#"
className="flex items-center p-2 pt-4 pb-4 text-white rounded-lg dark:text-white hover:bg-[#37A0EA] dark:hover:bg-[#37A0EA] group"
>
<svg
className="w-5 h-5"
viewBox="0 0 20 19"
fill="none" fill="none"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
<path <path
d="M8.19471 18.9791L7.72075 15.9932C7.42058 15.8826 7.10461 15.7325 6.77284 15.5429C6.44106 15.3534 6.14879 15.1559 5.89601 14.9505L3.09966 16.2302L0.895752 12.3437L3.45513 10.4716C3.42353 10.3294 3.40378 10.1675 3.39588 9.98579C3.38798 9.8041 3.38403 9.64217 3.38403 9.49998C3.38403 9.35779 3.38798 9.19586 3.39588 9.01417C3.40378 8.83249 3.42353 8.67055 3.45513 8.52837L0.895752 6.65623L3.09966 2.76977L5.89601 4.04946C6.14879 3.84408 6.44106 3.64659 6.77284 3.45701C7.10461 3.26743 7.42058 3.12524 7.72075 3.03045L8.19471 0.020813H12.5551L13.0291 3.00675C13.3293 3.11734 13.6492 3.26348 13.9889 3.44516C14.3285 3.62685 14.6168 3.82828 14.8538 4.04946L17.6502 2.76977L19.8541 6.65623L17.2947 8.48097C17.3263 8.63896 17.3461 8.80879 17.354 8.99048C17.3619 9.17216 17.3658 9.34199 17.3658 9.49998C17.3658 9.65797 17.3619 9.82385 17.354 9.99764C17.3461 10.1714 17.3263 10.3373 17.2947 10.4953L19.8541 12.3437L17.6502 16.2302L14.8538 14.9505C14.601 15.1559 14.3127 15.3573 13.9889 15.5548C13.665 15.7523 13.3451 15.8984 13.0291 15.9932L12.5551 18.9791H8.19471ZM10.3749 12.5807C11.228 12.5807 11.9548 12.2805 12.5551 11.6802C13.1555 11.0798 13.4556 10.3531 13.4556 9.49998C13.4556 8.64686 13.1555 7.92012 12.5551 7.31977C11.9548 6.71942 11.228 6.41925 10.3749 6.41925C9.52179 6.41925 8.79506 6.71942 8.19471 7.31977C7.59436 7.92012 7.29419 8.64686 7.29419 9.49998C7.29419 10.3531 7.59436 11.0798 8.19471 11.6802C8.79506 12.2805 9.52179 12.5807 10.3749 12.5807Z" d="M9.00016 2C5.68841 2 3.00016 4.68821 3.00016 8C3.00016 11.3118 5.68841 14 9.00016 14C12.3119 14 15.0002 11.3118 15.0002 8C15.0002 4.68821 12.3119 2 9.00016 2ZM9.00016 12C6.79006 12 5.00016 10.21 5.00016 8C5.00016 5.79 6.79006 4 9.00016 4C11.2103 4 13.0002 5.79 13.0002 8C13.0002 10.21 11.2103 12 9.00016 12Z"
fill="white" fill="currentColor"
/> />
</svg> </svg>
<span className="flex-1 ms-3 whitespace-nowrap">Settings</span> <span className="ms-3">Team</span>
</Link> </Link>
</li> </li>
<li> <li>
@@ -253,52 +234,21 @@ const Sidebar = () => {
className="flex items-center p-2 pt-4 pb-4 text-white rounded-lg dark:text-white hover:bg-[#37A0EA] dark:hover:bg-[#37A0EA] group" className="flex items-center p-2 pt-4 pb-4 text-white rounded-lg dark:text-white hover:bg-[#37A0EA] dark:hover:bg-[#37A0EA] group"
> >
<svg <svg
className="w-5 h-5 text-white dark:text-white self-center" className="w-5 h-5"
aria-hidden="true" viewBox="0 0 18 18"
fill="none"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
fill="currentColor"
viewBox="0 0 24 24"
> >
<path <path
fillRule="evenodd" d="M6.00002 2C5.44775 2 5.00002 2.44772 5.00002 3V15C5.00002 15.5523 5.44775 16 6.00002 16H12C12.5523 16 13 15.5523 13 15V3C13 2.44772 12.5523 2 12 2H6.00002Z"
d="M8.586 2.586A2 2 0 0 1 10 2h4a2 2 0 0 1 2 2v2h3a1 1 0 1 1 0 2v12a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V8a1 1 0 0 1 0-2h3V4a2 2 0 0 1 .586-1.414ZM10 6h4V4h-4v2Zm1 4a1 1 0 1 0-2 0v8a1 1 0 1 0 2 0v-8Zm4 0a1 1 0 1 0-2 0v8a1 1 0 1 0 2 0v-8Z" fill="currentColor"
clipRule="evenodd"
/> />
</svg> </svg>
<span className="flex-1 ms-3 whitespace-nowrap">Trash</span> <span className="ms-3">Admin</span>
</Link> </Link>
</li> </li>
</ul> </ul>
<div className="mt-6 p-4 rounded-lg text-white">
{/* Cloud Icon + Title */}
<div className="flex items-center">
<svg
className="w-7 h-7 text-white dark:text-white"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M13.383 4.076a6.5 6.5 0 0 0-6.887 3.95A5 5 0 0 0 7 18h3v-4a2 2 0 0 1-1.414-3.414l2-2a2 2 0 0 1 2.828 0l2 2A2 2 0 0 1 14 14v4h4a4 4 0 0 0 .988-7.876 6.5 6.5 0 0 0-5.605-6.048Z" />
<path d="M12.707 9.293a1 1 0 0 0-1.414 0l-2 2a1 1 0 1 0 1.414 1.414l.293-.293V19a1 1 0 1 0 2 0v-6.586l.293.293a1 1 0 0 0 1.414-1.414l-2-2Z" />
</svg>
<span className="ml-2 font-semibold">My Storage</span>
</div>
{/* Usage Text & Progress Bar */}
<p className="mt-2 text-lg">Used: of 100GB</p>
<div className="w-full bg-white rounded-full h-2 mt-2">
<div
className="bg-blue-500 h-2 rounded-full"
style={{ width: "24%" }}
></div>
</div>
</div>
</div> </div>
</aside> </aside>
</> </>
+111 -37
View File
@@ -1,14 +1,76 @@
import React from "react"; import React, { useState, useEffect } from "react";
import { FiEye, FiEyeOff } from "react-icons/fi"; import { FiEye, FiEyeOff } from "react-icons/fi";
import { Link } from "react-router-dom"; import { Link, useNavigate } from "react-router-dom";
import toast from "react-hot-toast"; // Import React Hot Toast
const API_URL = import.meta.env.VITE_API_URL; // Using .env variable
const Login = () => { const Login = () => {
const [showPassword, setShowPassword] = React.useState(false); const [showPassword, setShowPassword] = useState(false);
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [loading, setLoading] = useState(false);
const navigate = useNavigate(); // For navigation
useEffect(() => {
// Check if token is present in localStorage and redirect to Dashboard
if (localStorage.getItem("token")) {
navigate("/dashboard"); // Redirect to Dashboard
}
}, [navigate]);
const togglePassword = () => { const togglePassword = () => {
setShowPassword(!showPassword); setShowPassword(!showPassword);
}; };
const handleSubmit = async (e) => {
e.preventDefault();
setLoading(true);
// Show loading toast
const toastId = toast.loading("Logging in...");
try {
const response = await fetch(`${API_URL}/api/login`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
email,
password,
}),
});
const data = await response.json();
// Dismiss the loading toast after the response
toast.dismiss(toastId);
if (response.ok) {
// On success, store the token in localStorage
localStorage.setItem("token", data.token);
localStorage.setItem("expiresIn", data.expiresIn);
// Show success toast
toast.success("Login successful!");
// Redirect to Dashboard
navigate("/dashboard");
} else {
// Show error toast if login fails
toast.error(data.message || "Login failed.");
}
} catch (error) {
// Dismiss the loading toast and show error
toast.dismiss(toastId);
toast.error("An error occurred. Please try again.");
} finally {
setLoading(false);
}
};
return ( return (
<div className="min-h-screen bg-gray-100 flex items-center justify-center p-4"> <div className="min-h-screen bg-gray-100 flex items-center justify-center p-4">
<div className="w-full max-w-md bg-white rounded-4xl shadow-lg p-8"> <div className="w-full max-w-md bg-white rounded-4xl shadow-lg p-8">
@@ -16,44 +78,56 @@ const Login = () => {
Log in Log in
</h1> </h1>
<div className="mb-4"> <form onSubmit={handleSubmit}>
<div className="flex items-center"> <div className="mb-4">
<input <div className="flex items-center">
type="email" <input
id="email" type="email"
placeholder="Enter your email" id="email"
className="w-full border border-gray-300 rounded-l-lg px-4 py-4 focus:outline-none focus:border-blue-500" placeholder="Enter your email"
/> className="w-full border border-gray-300 rounded-l-lg px-4 py-4 focus:outline-none focus:border-blue-500"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
</div>
</div> </div>
</div> <div className="mb-1">
<div className="mb-1"> <div className="relative">
<div className="relative"> <input
<input type={showPassword ? "text" : "password"}
type={showPassword ? "text" : "password"} id="password"
id="password" placeholder="Enter your password"
placeholder="Enter your password" className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:border-blue-500 pr-10"
className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:border-blue-500 pr-10" value={password}
/> onChange={(e) => setPassword(e.target.value)}
<button required
type="button" />
onClick={togglePassword} <button
className="absolute right-2 top-4 text-2xl text-gray-500 hover:text-gray-700" type="button"
onClick={togglePassword}
className="absolute right-2 top-4 text-2xl text-gray-500 hover:text-gray-700"
>
{showPassword ? <FiEyeOff /> : <FiEye />}
</button>
</div>
</div>
<div className="mb-6 ">
<Link
to="#!"
className="text-sm text-blue-600 hover:underline inline-block"
> >
{showPassword ? <FiEyeOff /> : <FiEye />} Forgot password?
</button> </Link>
</div> </div>
</div> <button
<div className="mb-6 "> type="submit"
<Link disabled={loading}
to="#!" className="w-full py-3 bg-gradient-to-r from-[#1877F2] to-[#0E458C] hover:from-[#0E458C] hover:to-[#1877F2] text-white font-semibold rounded-full shadow-md transition duration-300"
className="text-sm text-blue-600 hover:underline inline-block"
> >
Forgot password? {loading ? "Logging In..." : "Login"}
</Link> </button>
</div> </form>
<button className="w-full py-3 bg-gradient-to-r from-[#1877F2] to-[#0E458C] hover:from-[#0E458C] hover:to-[#1877F2] text-white font-semibold rounded-full shadow-md transition duration-300">
Login
</button>
<div className="text-center mt-6"> <div className="text-center mt-6">
<p className="text-gray-700"> <p className="text-gray-700">
Dont have an account?{" "} Dont have an account?{" "}
+184 -9
View File
@@ -1,41 +1,115 @@
// eslint-disable-next-line no-unused-vars
import React, { useState } from "react"; import React, { useState } from "react";
import { FiEye, FiEyeOff } from "react-icons/fi"; import { FiEye, FiEyeOff } from "react-icons/fi";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import toast, { Toaster } from "react-hot-toast";
// const API_BASE_URL = process.env.REACT_APP_API_URL;
const API_BASE_URL = import.meta.env.VITE_API_URL;
const SignUp = () => { const SignUp = () => {
const [formData, setFormData] = useState({
firstname: "",
lastname: "",
email: "",
password: "",
confirmPassword: "",
});
const [showPassword, setShowPassword] = useState(false); const [showPassword, setShowPassword] = useState(false);
const [showConfirmPassword, setShowConfirmPassword] = useState(false); const [showConfirmPassword, setShowConfirmPassword] = useState(false);
const [loading, setLoading] = useState(false);
const handleChange = (e) => {
setFormData((prev) => ({
...prev,
[e.target.name]: e.target.value,
}));
};
const handleSubmit = async (e) => {
e.preventDefault();
if (formData.password !== formData.confirmPassword) {
toast.error("Passwords do not match.");
return;
}
try {
setLoading(true);
const toastId = toast.loading("Registering...");
const response = await fetch(`${API_BASE_URL}/api/signup`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
firstname: formData.firstname,
lastname: formData.lastname,
email: formData.email,
password: formData.password,
}),
});
const data = await response.json();
if (response.ok) {
toast.success("Successfully registered!", { id: toastId });
// Optionally redirect to login
} else {
toast.error(data.message || "Signup failed.", { id: toastId });
}
} catch (error) {
toast.error("An error occurred. Please try again.");
} finally {
setLoading(false);
}
};
return ( return (
<div className="min-h-screen bg-gray-100 flex items-center justify-center p-6"> <div className="min-h-screen bg-gray-100 flex items-center justify-center p-6">
<Toaster position="top-right" />
<div className="w-full max-w-md bg-white rounded-2xl shadow-lg p-8"> <div className="w-full max-w-md bg-white rounded-2xl shadow-lg p-8">
<h1 className="text-2xl font-bold text-gray-900 mb-6">Sign Up</h1> <h1 className="text-2xl font-bold text-gray-900 mb-6">Sign Up</h1>
{/* Form Fields */} <form className="space-y-4" onSubmit={handleSubmit}>
<div className="space-y-4">
<input <input
type="text" type="text"
name="firstname"
placeholder="First Name" placeholder="First Name"
value={formData.firstname}
onChange={handleChange}
className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:ring-2 focus:ring-blue-500" className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:ring-2 focus:ring-blue-500"
required
/> />
<input <input
type="text" type="text"
name="lastname"
placeholder="Last Name" placeholder="Last Name"
value={formData.lastname}
onChange={handleChange}
className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:ring-2 focus:ring-blue-500" className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:ring-2 focus:ring-blue-500"
required
/> />
<input <input
type="email" type="email"
name="email"
placeholder="Enter your email" placeholder="Enter your email"
value={formData.email}
onChange={handleChange}
className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:ring-2 focus:ring-blue-500" className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:ring-2 focus:ring-blue-500"
required
/> />
{/* Password Field */} {/* Password Field */}
<div className="relative"> <div className="relative">
<input <input
type={showPassword ? "text" : "password"} type={showPassword ? "text" : "password"}
name="password"
placeholder="Enter your password" placeholder="Enter your password"
value={formData.password}
onChange={handleChange}
className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:ring-2 focus:ring-blue-500 pr-10" className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:ring-2 focus:ring-blue-500 pr-10"
required
/> />
<button <button
type="button" type="button"
@@ -50,8 +124,12 @@ const SignUp = () => {
<div className="relative"> <div className="relative">
<input <input
type={showConfirmPassword ? "text" : "password"} type={showConfirmPassword ? "text" : "password"}
name="confirmPassword"
placeholder="Confirm your password" placeholder="Confirm your password"
value={formData.confirmPassword}
onChange={handleChange}
className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:ring-2 focus:ring-blue-500 pr-10" className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:ring-2 focus:ring-blue-500 pr-10"
required
/> />
<button <button
type="button" type="button"
@@ -61,12 +139,20 @@ const SignUp = () => {
{showConfirmPassword ? <FiEyeOff /> : <FiEye />} {showConfirmPassword ? <FiEyeOff /> : <FiEye />}
</button> </button>
</div> </div>
</div>
{/* Sign Up Button */} {/* Sign Up Button */}
<button className="w-full mt-6 py-3 bg-gradient-to-r from-[#10B981] to-[#07533A] hover:from-[#0E458C] hover:to-[#1877F2] text-white font-semibold rounded-lg shadow-md transition duration-300"> <button
Sign Up type="submit"
</button> disabled={loading}
className={`w-full mt-4 py-3 ${
loading
? "bg-gray-400 cursor-not-allowed"
: "bg-gradient-to-r from-[#10B981] to-[#07533A] hover:from-[#0E458C] hover:to-[#1877F2]"
} text-white font-semibold rounded-lg shadow-md transition duration-300`}
>
{loading ? "Signing Up..." : "Sign Up"}
</button>
</form>
{/* Redirect to Login */} {/* Redirect to Login */}
<p className="text-center mt-4 text-gray-700"> <p className="text-center mt-4 text-gray-700">
@@ -84,3 +170,92 @@ const SignUp = () => {
}; };
export default SignUp; export default SignUp;
// // eslint-disable-next-line no-unused-vars
// import React, { useState } from "react";
// import { FiEye, FiEyeOff } from "react-icons/fi";
// import { Link } from "react-router-dom";
// const SignUp = () => {
// const [showPassword, setShowPassword] = useState(false);
// const [showConfirmPassword, setShowConfirmPassword] = useState(false);
// return (
// <div className="min-h-screen bg-gray-100 flex items-center justify-center p-6">
// <div className="w-full max-w-md bg-white rounded-2xl shadow-lg p-8">
// <h1 className="text-2xl font-bold text-gray-900 mb-6">Sign Up</h1>
// {/* Form Fields */}
// <div className="space-y-4">
// <input
// type="text"
// placeholder="First Name"
// className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:ring-2 focus:ring-blue-500"
// />
// <input
// type="text"
// placeholder="Last Name"
// className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:ring-2 focus:ring-blue-500"
// />
// <input
// type="email"
// placeholder="Enter your email"
// className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:ring-2 focus:ring-blue-500"
// />
// {/* Password Field */}
// <div className="relative">
// <input
// type={showPassword ? "text" : "password"}
// placeholder="Enter your password"
// className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:ring-2 focus:ring-blue-500 pr-10"
// />
// <button
// type="button"
// onClick={() => setShowPassword(!showPassword)}
// className="absolute right-3 top-4 text-2xl text-gray-500 hover:text-gray-700"
// >
// {showPassword ? <FiEyeOff /> : <FiEye />}
// </button>
// </div>
// {/* Confirm Password Field */}
// <div className="relative">
// <input
// type={showConfirmPassword ? "text" : "password"}
// placeholder="Confirm your password"
// className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:ring-2 focus:ring-blue-500 pr-10"
// />
// <button
// type="button"
// onClick={() => setShowConfirmPassword(!showConfirmPassword)}
// className="absolute right-3 top-4 text-2xl text-gray-500 hover:text-gray-700"
// >
// {showConfirmPassword ? <FiEyeOff /> : <FiEye />}
// </button>
// </div>
// </div>
// {/* Sign Up Button */}
// <button className="w-full mt-6 py-3 bg-gradient-to-r from-[#10B981] to-[#07533A] hover:from-[#0E458C] hover:to-[#1877F2] text-white font-semibold rounded-lg shadow-md transition duration-300">
// Sign Up
// </button>
// {/* Redirect to Login */}
// <p className="text-center mt-4 text-gray-700">
// Already have an account?{" "}
// <Link
// to="/login"
// className="text-blue-500 hover:underline font-medium"
// >
// Login
// </Link>
// </p>
// </div>
// </div>
// );
// };
// export default SignUp;