diff --git a/index.html b/index.html index b6c9d18..248a71f 100644 --- a/index.html +++ b/index.html @@ -2,7 +2,11 @@ - + + + + + diff --git a/package-lock.json b/package-lock.json index 5365317..51bc829 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "@react-jvectormap/core": "^1.0.4", "@react-jvectormap/world": "^1.1.2", "apexcharts": "^4.5.0", + "axios": "^1.8.4", "clsx": "^2.1.1", "flatpickr": "^4.6.13", "react": "^19.0.0", @@ -2376,6 +2377,12 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "license": "Python-2.0" }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, "node_modules/attr-accept": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.5.tgz", @@ -2385,6 +2392,17 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", + "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -2448,6 +2466,19 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2541,6 +2572,18 @@ "dev": true, "license": "MIT" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2635,6 +2678,15 @@ "dev": true, "license": "MIT" }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/detect-libc": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", @@ -2666,6 +2718,20 @@ "tslib": "^2.0.3" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.136", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.136.tgz", @@ -2707,6 +2773,51 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/esbuild": { "version": "0.25.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", @@ -3095,6 +3206,41 @@ "dev": true, "license": "ISC" }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -3109,6 +3255,15 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -3118,6 +3273,43 @@ "node": ">=6.9.0" } }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -3144,6 +3336,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -3168,6 +3372,45 @@ "node": ">=8" } }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -3665,6 +3908,15 @@ "yallist": "^3.0.2" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -3689,6 +3941,27 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -3945,6 +4218,12 @@ "react-is": "^16.13.1" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", diff --git a/package.json b/package.json index addf4c6..13de9f9 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "@react-jvectormap/core": "^1.0.4", "@react-jvectormap/world": "^1.1.2", "apexcharts": "^4.5.0", + "axios": "^1.8.4", "clsx": "^2.1.1", "flatpickr": "^4.6.13", "react": "^19.0.0", diff --git a/public/api/login.php b/public/api/login.php new file mode 100644 index 0000000..99bd55f --- /dev/null +++ b/public/api/login.php @@ -0,0 +1,45 @@ + 'password123', + 'user2@example.com' => 'securepassword', +]; + +// Read input data +$input = json_decode(file_get_contents('php://input'), true); +$email = $input['email'] ?? ''; +$password = $input['password'] ?? ''; + +// Validate input +if (empty($email) || empty($password)) { + http_response_code(400); + echo json_encode(['error' => 'Email and password are required.']); + exit; +} + +// Authenticate user +if (isset($users[$email]) && $users[$email] === $password) { + // Generate a token (for simplicity, using base64 encoding) + $token = base64_encode($email . ':' . time()); + + echo json_encode(['token' => $token]); +} else { + http_response_code(401); + echo json_encode(['error' => 'Invalid email or password.']); +} +?> \ No newline at end of file diff --git a/public/favicon.png b/public/favicon.png deleted file mode 100644 index f64b07e..0000000 Binary files a/public/favicon.png and /dev/null differ diff --git a/public/favicon/apple-touch-icon.png b/public/favicon/apple-touch-icon.png new file mode 100644 index 0000000..0f80faa Binary files /dev/null and b/public/favicon/apple-touch-icon.png differ diff --git a/public/favicon/favicon-96x96.png b/public/favicon/favicon-96x96.png new file mode 100644 index 0000000..fe82811 Binary files /dev/null and b/public/favicon/favicon-96x96.png differ diff --git a/public/favicon/favicon.ico b/public/favicon/favicon.ico new file mode 100644 index 0000000..8f02ce1 Binary files /dev/null and b/public/favicon/favicon.ico differ diff --git a/public/favicon/favicon.svg b/public/favicon/favicon.svg new file mode 100644 index 0000000..17359b1 --- /dev/null +++ b/public/favicon/favicon.svg @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/public/favicon/site.webmanifest b/public/favicon/site.webmanifest new file mode 100644 index 0000000..ccf313a --- /dev/null +++ b/public/favicon/site.webmanifest @@ -0,0 +1,21 @@ +{ + "name": "MyWebSite", + "short_name": "MySite", + "icons": [ + { + "src": "/web-app-manifest-192x192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "/web-app-manifest-512x512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} \ No newline at end of file diff --git a/public/favicon/web-app-manifest-192x192.png b/public/favicon/web-app-manifest-192x192.png new file mode 100644 index 0000000..a402777 Binary files /dev/null and b/public/favicon/web-app-manifest-192x192.png differ diff --git a/public/favicon/web-app-manifest-512x512.png b/public/favicon/web-app-manifest-512x512.png new file mode 100644 index 0000000..56aeab8 Binary files /dev/null and b/public/favicon/web-app-manifest-512x512.png differ diff --git a/public/images/logo.webp b/public/images/logo.webp new file mode 100644 index 0000000..6f0ffcc Binary files /dev/null and b/public/images/logo.webp differ diff --git a/public/images/logo_dark.webp b/public/images/logo_dark.webp new file mode 100644 index 0000000..065a992 Binary files /dev/null and b/public/images/logo_dark.webp differ diff --git a/public/images/logo_icon.webp b/public/images/logo_icon.webp new file mode 100644 index 0000000..7d3ddd9 Binary files /dev/null and b/public/images/logo_icon.webp differ diff --git a/src/components/auth/Login.tsx b/src/components/auth/Login.tsx index 967e52e..3f8da27 100644 --- a/src/components/auth/Login.tsx +++ b/src/components/auth/Login.tsx @@ -1,63 +1,97 @@ import React, { useState } from 'react'; +import { useAuth } from '../../context/AuthContext'; +import { API_BASE_URL } from '../../config/apiConfig'; -interface LoginProps { - onLogin: (token: string) => void; // Callback to handle successful login -} - -const Login: React.FC = ({ onLogin }) => { - const [username, setUsername] = useState(''); +const Login: React.FC = () => { + const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [error, setError] = useState(''); + const [isLoading, setIsLoading] = useState(false); + const { login } = useAuth(); - // Hardcoded credentials - const hardcodedUsername = 'admin'; - const hardcodedPassword = 'password123'; - - const handleSubmit = (e: React.FormEvent) => { + const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setError(''); + setIsLoading(true); - // Validate hardcoded credentials - if (username === hardcodedUsername && password === hardcodedPassword) { - const token = 'hardcoded-token'; // Generate a mock token - onLogin(token); // Pass the token to the parent component - } else { - setError('Invalid username or password'); + try { + // Send credentials as JSON + const response = await fetch(`${API_BASE_URL}/api/login.php`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + email, + password, + }), + }); + + const data = await response.json(); + + if (response.ok) { + // If response is successful, we should have a token + if (data.token) { + login(data.token); + } else { + setError('Invalid server response'); + } + } else { + // Handle error responses + setError(data.error || 'Authentication failed'); + } + } catch (err) { + setError('Failed to connect to the server. Please try again.'); + console.error('Login error:', err); + } finally { + setIsLoading(false); } }; return ( -
-

Login

- {error &&

{error}

} -
-
- - setUsername(e.target.value)} - className="w-full px-3 py-2 border rounded" - required +
+
+
+ Logo
-
- - setPassword(e.target.value)} - className="w-full px-3 py-2 border rounded" - required - /> -
- - +

Login

+ {error &&

{error}

} +
+
+ + setEmail(e.target.value)} + className="w-full px-3 py-2 border rounded" + required + disabled={isLoading} + /> +
+
+ + setPassword(e.target.value)} + className="w-full px-3 py-2 border rounded" + required + disabled={isLoading} + /> +
+ +
+
); }; diff --git a/src/components/auth/SignInForm.tsx b/src/components/auth/SignInForm.tsx index 72f4915..5da1465 100644 --- a/src/components/auth/SignInForm.tsx +++ b/src/components/auth/SignInForm.tsx @@ -24,7 +24,7 @@ export default function SignInForm() {

- Sign In + Sign In2

Enter your email and password to sign in! diff --git a/src/components/dashboard/DeployList.tsx b/src/components/dashboard/DeployList.tsx index f238d6a..fbd316d 100644 --- a/src/components/dashboard/DeployList.tsx +++ b/src/components/dashboard/DeployList.tsx @@ -1,4 +1,5 @@ import React, { useState, useRef, useEffect } from 'react'; +import { API_BASE_URL } from '../../config/apiConfig'; interface DeployListProps { setLog: React.Dispatch>; @@ -48,7 +49,7 @@ const DeployList: React.FC = ({ setLog, token }) => { setStatus((prev) => ({ ...prev, [project]: 'Migrating...' })); setLog(''); // Clear the log before starting try { - const response = await fetch('https://deploy.projekti.info/api/migrate.php', { + const response = await fetch(`${API_BASE_URL}/api/migrate.php`, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -78,7 +79,7 @@ const DeployList: React.FC = ({ setLog, token }) => { setStatus((prev) => ({ ...prev, [project]: 'Deploying...' })); setLog(''); // Clear the log before starting try { - const response = await fetch('https://deploy.projekti.info/api/deploy.php', { + const response = await fetch(`${API_BASE_URL}/api/deploy.php`, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -113,12 +114,13 @@ const DeployList: React.FC = ({ setLog, token }) => { if (!window.confirm(`Are you sure you want to back up ${type} for ${project}?`)) { return; // Exit if user cancels } + setDropdownOpen(prev => ({ ...prev, [project]: false })); setStatus(prev => ({ ...prev, [project]: `Backing up ${type}...` })); setLog(''); // Clear the log before starting try { - const response = await fetch('https://deploy.projekti.info/api/backup.php', { + const response = await fetch(`${API_BASE_URL}/api/backup.php`, { method: 'POST', headers: { 'Content-Type': 'application/json', diff --git a/src/components/header/Header.tsx b/src/components/header/Header.tsx index 37613ad..b949cf6 100644 --- a/src/components/header/Header.tsx +++ b/src/components/header/Header.tsx @@ -81,12 +81,12 @@ const Header: React.FC = ({ onClick, onToggle }) => { Logo Logo diff --git a/src/components/header/UserDropdown.tsx b/src/components/header/UserDropdown.tsx index 63d5eb7..97f3735 100644 --- a/src/components/header/UserDropdown.tsx +++ b/src/components/header/UserDropdown.tsx @@ -1,10 +1,11 @@ import { useState } from "react"; import { DropdownItem } from "../ui/dropdown/DropdownItem"; import { Dropdown } from "../ui/dropdown/Dropdown"; -import { Link } from "react-router"; +import { useAuth } from "../../context/AuthContext"; export default function UserDropdown() { const [isOpen, setIsOpen] = useState(false); + const { logout } = useAuth(); function toggleDropdown() { setIsOpen(!isOpen); @@ -13,6 +14,12 @@ export default function UserDropdown() { function closeDropdown() { setIsOpen(false); } + + function handleSignOut() { + logout(); + window.location.href = "/"; // Redirect to the login page + } + return (

); diff --git a/src/config/apiConfig.ts b/src/config/apiConfig.ts new file mode 100644 index 0000000..b4d7075 --- /dev/null +++ b/src/config/apiConfig.ts @@ -0,0 +1,2 @@ +// Base URL configuration for API endpoints +export const API_BASE_URL = 'https://deploy.projekti.info'; \ No newline at end of file diff --git a/src/context/AuthContext.tsx b/src/context/AuthContext.tsx new file mode 100644 index 0000000..0ec43cd --- /dev/null +++ b/src/context/AuthContext.tsx @@ -0,0 +1,37 @@ +import React, { createContext, useContext, useState, ReactNode } from 'react'; + +interface AuthContextType { + token: string | null; + login: (token: string) => void; + logout: () => void; +} + +const AuthContext = createContext(undefined); + +export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => { + const [token, setToken] = useState(localStorage.getItem('token')); + + const login = (newToken: string) => { + setToken(newToken); + localStorage.setItem('token', newToken); + }; + + const logout = () => { + setToken(null); + localStorage.removeItem('token'); + }; + + return ( + + {children} + + ); +}; + +export const useAuth = (): AuthContextType => { + const context = useContext(AuthContext); + if (!context) { + throw new Error('useAuth must be used within an AuthProvider'); + } + return context; +}; \ No newline at end of file diff --git a/src/layout/AppHeader.tsx b/src/layout/AppHeader.tsx index c2d8b50..0d3d019 100644 --- a/src/layout/AppHeader.tsx +++ b/src/layout/AppHeader.tsx @@ -49,32 +49,49 @@ const AppHeader: React.FC = () => { onClick={handleToggle} aria-label="Toggle Sidebar" > - - - + {isMobileOpen ? ( + + + + ) : ( + + + + )} + {/* Cross Icon */} Logo Logo @@ -84,7 +101,6 @@ const AppHeader: React.FC = () => { className="flex items-center justify-center w-10 h-10 text-gray-700 rounded-lg z-99999 hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-gray-800 lg:hidden" > { const { isExpanded, isHovered, isMobileOpen } = useSidebar(); @@ -19,7 +21,7 @@ const LayoutContent: React.FC = () => { } ${isMobileOpen ? "ml-0" : ""}`} > -
+
@@ -28,6 +30,12 @@ const LayoutContent: React.FC = () => { }; const AppLayout: React.FC = () => { + const { token } = useAuth(); + + if (!token) { + return ; + } + return ( diff --git a/src/layout/AppSidebar.tsx b/src/layout/AppSidebar.tsx index c1b1a0b..15877af 100644 --- a/src/layout/AppSidebar.tsx +++ b/src/layout/AppSidebar.tsx @@ -81,14 +81,6 @@ const othersItems: NavItem[] = [ { name: "Videos", path: "/videos", pro: false }, ], }, - { - icon: , - name: "Authentication", - subItems: [ - { name: "Sign In", path: "/signin", pro: false }, - { name: "Sign Up", path: "/signup", pro: false }, - ], - }, ]; const AppSidebar: React.FC = () => { @@ -307,14 +299,14 @@ const AppSidebar: React.FC = () => { <> Logo Logo { ) : ( Logo {

{

- - - - - + + + + + + + , );