User Auth Done
This commit is contained in:
117
src/App.js
117
src/App.js
@@ -1,4 +1,6 @@
|
||||
import React, { useState } from "react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { BrowserRouter as Router, Routes, Route, useNavigate } from "react-router-dom";
|
||||
import axios from "axios";
|
||||
import Chart from "./components/Chart";
|
||||
import Scanner from "./components/Scanner";
|
||||
import Ribbon from "./components/Ribbon";
|
||||
@@ -7,52 +9,76 @@ import Popup from "./components/Popup";
|
||||
import AboutScanner from "./components/AboutScanner";
|
||||
import MarketIntel from "./components/MarketIntel";
|
||||
import TrendScanner from "./components/TrendScanner";
|
||||
import Login from "./components/Login";
|
||||
import Signup from "./components/Signup";
|
||||
import ProtectedRoute from "./components/ProtectedRoute";
|
||||
import Profile from "./components/Profile";
|
||||
import "./index.css";
|
||||
|
||||
function App() {
|
||||
function Dashboard() {
|
||||
const [selectedStock, setSelectedStock] = useState("NSE:RELIANCE");
|
||||
const [signal, setSignal] = useState(null);
|
||||
const [view, setView] = useState("scanner");
|
||||
const [userProfile, setUserProfile] = useState(null);
|
||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const fetchProfile = () => {
|
||||
axios.get("http://127.0.0.1:8000/api/auth/me")
|
||||
.then(res => setUserProfile(res.data))
|
||||
.catch(err => {
|
||||
if (err.response?.status === 401) handleLogout();
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchProfile();
|
||||
}, []);
|
||||
|
||||
const handleLogout = () => {
|
||||
localStorage.removeItem("token");
|
||||
navigate("/login");
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="app-container">
|
||||
<h1>📈 Stock Scanner Pro</h1>
|
||||
{/* HEADER */}
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '2rem', position: 'relative' }}>
|
||||
<h1 style={{ margin: 0 }}>📈 Stock Scanner Pro</h1>
|
||||
|
||||
{userProfile && (
|
||||
<div style={{ position: 'relative' }}>
|
||||
<div
|
||||
style={{ display: 'flex', alignItems: 'center', gap: '12px', cursor: 'pointer', background: 'var(--glass-bg)', padding: '8px 16px', borderRadius: '30px', border: '1px solid var(--border-color)', backdropFilter: 'blur(10px)' }}
|
||||
onClick={() => setDropdownOpen(!dropdownOpen)}
|
||||
>
|
||||
<div style={{ width: '35px', height: '35px', borderRadius: '50%', background: 'linear-gradient(135deg, var(--accent-blue), var(--accent-green))', display: 'flex', justifyContent: 'center', alignItems: 'center', fontWeight: 'bold', fontSize: '1.2rem', color: 'white' }}>
|
||||
{userProfile.display_name ? userProfile.display_name.charAt(0).toUpperCase() : '?'}
|
||||
</div>
|
||||
<span style={{ fontWeight: 600 }}>{userProfile.display_name || userProfile.username}</span>
|
||||
<span style={{ fontSize: '0.8rem', color: 'var(--text-secondary)' }}>▼</span>
|
||||
</div>
|
||||
|
||||
{dropdownOpen && (
|
||||
<div className="glass-card animate-fade-in" style={{ position: 'absolute', top: '55px', right: '0', width: '220px', zIndex: 100, padding: '10px' }}>
|
||||
<button className="nav-btn" style={{ width: '100%', marginBottom: '8px', border: 'none', whiteSpace: 'nowrap' }} onClick={() => { setView('profile'); setDropdownOpen(false); }}>
|
||||
⚙️ Profile Settings
|
||||
</button>
|
||||
<button className="nav-btn" style={{ width: '100%', border: 'none', background: 'var(--accent-red-bg)', color: 'var(--accent-red)', whiteSpace: 'nowrap' }} onClick={handleLogout}>
|
||||
🚪 Logout
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="nav-container">
|
||||
<button
|
||||
className={`nav-btn ${view === "scanner" ? "active" : ""}`}
|
||||
onClick={() => setView("scanner")}
|
||||
>
|
||||
📊 Scanner
|
||||
</button>
|
||||
|
||||
<button
|
||||
className={`nav-btn ${view === "ribbon" ? "active" : ""}`}
|
||||
onClick={() => setView("ribbon")}
|
||||
>
|
||||
📈 Ribbon Strategy
|
||||
</button>
|
||||
|
||||
<button
|
||||
className={`nav-btn ${view === "smartmoney" ? "active" : ""}`}
|
||||
onClick={() => setView("smartmoney")}
|
||||
>
|
||||
🏦 Smart Money
|
||||
</button>
|
||||
|
||||
<button
|
||||
className={`nav-btn ${view === "trend" ? "active" : ""}`}
|
||||
onClick={() => setView("trend")}
|
||||
>
|
||||
⚡ Trend Analysis
|
||||
</button>
|
||||
|
||||
<button
|
||||
className={`nav-btn ${view === "marketintel" ? "active" : ""}`}
|
||||
onClick={() => setView("marketintel")}
|
||||
>
|
||||
📰 Market Intel
|
||||
</button>
|
||||
<button className={`nav-btn ${view === "scanner" ? "active" : ""}`} onClick={() => setView("scanner")}>📊 Scanner</button>
|
||||
<button className={`nav-btn ${view === "ribbon" ? "active" : ""}`} onClick={() => setView("ribbon")}>📈 Ribbon Strategy</button>
|
||||
<button className={`nav-btn ${view === "smartmoney" ? "active" : ""}`} onClick={() => setView("smartmoney")}>🏦 Smart Money</button>
|
||||
<button className={`nav-btn ${view === "trend" ? "active" : ""}`} onClick={() => setView("trend")}>⚡ Trend Analysis</button>
|
||||
<button className={`nav-btn ${view === "marketintel" ? "active" : ""}`} onClick={() => setView("marketintel")}>📰 Market Intel</button>
|
||||
</div>
|
||||
|
||||
<div className="animate-fade-in">
|
||||
@@ -67,17 +93,26 @@ function App() {
|
||||
{signal && <Popup signal={signal} />}
|
||||
</>
|
||||
)}
|
||||
|
||||
{view === "ribbon" && <Ribbon />}
|
||||
|
||||
{view === "smartmoney" && <SmartMoney />}
|
||||
|
||||
{view === "marketintel" && <MarketIntel />}
|
||||
|
||||
{view === "trend" && <TrendScanner />}
|
||||
{view === "profile" && <Profile userProfile={userProfile} onProfileUpdated={fetchProfile} />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<Router>
|
||||
<Routes>
|
||||
<Route path="/login" element={<Login />} />
|
||||
<Route path="/signup" element={<Signup />} />
|
||||
<Route path="/" element={<ProtectedRoute><Dashboard /></ProtectedRoute>} />
|
||||
</Routes>
|
||||
</Router>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
70
src/components/Login.js
Normal file
70
src/components/Login.js
Normal file
@@ -0,0 +1,70 @@
|
||||
import React, { useState } from "react";
|
||||
import axios from "axios";
|
||||
import { useNavigate, Link } from "react-router-dom";
|
||||
import forge from "node-forge";
|
||||
|
||||
function Login() {
|
||||
const [username, setUsername] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [error, setError] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleLogin = async (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
setError("");
|
||||
try {
|
||||
const resKey = await axios.get("http://127.0.0.1:8000/api/auth/public-key");
|
||||
const publicKeyPem = resKey.data.public_key;
|
||||
const publicKey = forge.pki.publicKeyFromPem(publicKeyPem);
|
||||
const encrypted = publicKey.encrypt(password, 'RSA-OAEP');
|
||||
const encryptedBase64 = forge.util.encode64(encrypted);
|
||||
|
||||
const res = await axios.post("http://127.0.0.1:8000/api/auth/login", {
|
||||
username,
|
||||
password: encryptedBase64
|
||||
});
|
||||
|
||||
localStorage.setItem("token", res.data.access_token);
|
||||
navigate("/");
|
||||
} catch (err) {
|
||||
setError(err.response?.data?.detail || "Login failed");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="app-container" style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh' }}>
|
||||
<div className="glass-card" style={{ width: '400px', textAlign: 'center' }}>
|
||||
<h2 className="text-blue">📈 Login</h2>
|
||||
<form onSubmit={handleLogin} style={{ display: 'flex', flexDirection: 'column', gap: '15px' }}>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Username"
|
||||
value={username}
|
||||
onChange={e => setUsername(e.target.value)}
|
||||
style={{ padding: '10px', borderRadius: '5px', border: '1px solid var(--border-color)', background: 'var(--bg-tertiary)', color: 'white' }}
|
||||
required
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
value={password}
|
||||
onChange={e => setPassword(e.target.value)}
|
||||
style={{ padding: '10px', borderRadius: '5px', border: '1px solid var(--border-color)', background: 'var(--bg-tertiary)', color: 'white' }}
|
||||
required
|
||||
/>
|
||||
{error && <p className="text-red">{error}</p>}
|
||||
<button type="submit" className="nav-btn active" style={{ justifyContent: 'center' }} disabled={loading}>
|
||||
{loading ? "Logging in..." : "Login"}
|
||||
</button>
|
||||
</form>
|
||||
<p style={{ marginTop: '20px' }}>Don't have an account? <Link to="/signup" className="text-blue">Sign up</Link></p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Login;
|
||||
@@ -10,7 +10,7 @@ function MarketIntel() {
|
||||
|
||||
const loadData = async () => {
|
||||
try {
|
||||
const res = await axios.get("http://127.0.0.1:8000/marketintel");
|
||||
const res = await axios.get("http://127.0.0.1:8000/api/scanner/marketintel");
|
||||
setData(res.data);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
173
src/components/Profile.js
Normal file
173
src/components/Profile.js
Normal file
@@ -0,0 +1,173 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import forge from "node-forge";
|
||||
|
||||
function Profile({ userProfile, onProfileUpdated }) {
|
||||
const [formData, setFormData] = useState({
|
||||
display_name: "",
|
||||
email_id: "",
|
||||
mobile_no: "",
|
||||
gender: "",
|
||||
password: ""
|
||||
});
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [message, setMessage] = useState("");
|
||||
const [error, setError] = useState("");
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (userProfile) {
|
||||
setFormData({
|
||||
display_name: userProfile.display_name || "",
|
||||
email_id: userProfile.email_id || "",
|
||||
mobile_no: userProfile.mobile_no || "",
|
||||
gender: userProfile.gender || "",
|
||||
password: "" // password field is blank initially
|
||||
});
|
||||
}
|
||||
}, [userProfile]);
|
||||
|
||||
const handleChange = (e) => {
|
||||
setFormData({ ...formData, [e.target.name]: e.target.value });
|
||||
};
|
||||
|
||||
const handleSave = async (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
setMessage("");
|
||||
setError("");
|
||||
|
||||
try {
|
||||
const payload = { ...formData };
|
||||
|
||||
if (payload.password) {
|
||||
// Fetch Public Key to encrypt new password
|
||||
const resKey = await axios.get("http://127.0.0.1:8000/api/auth/public-key");
|
||||
const publicKey = forge.pki.publicKeyFromPem(resKey.data.public_key);
|
||||
const encrypted = publicKey.encrypt(payload.password, 'RSA-OAEP');
|
||||
payload.password = forge.util.encode64(encrypted);
|
||||
} else {
|
||||
delete payload.password;
|
||||
}
|
||||
|
||||
await axios.put("http://127.0.0.1:8000/api/auth/update-profile", payload);
|
||||
setMessage("Profile updated successfully! ✅");
|
||||
if (onProfileUpdated) onProfileUpdated();
|
||||
setFormData(prev => ({ ...prev, password: "" })); // Clear password field
|
||||
} catch (err) {
|
||||
setError(err.response?.data?.detail || "Failed to update profile");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (!userProfile) return <div className="glass-card text-center">Loading Profile...</div>;
|
||||
|
||||
return (
|
||||
<div className="animate-fade-in" style={{ display: 'flex', justifyContent: 'center', marginTop: '20px' }}>
|
||||
<div className="glass-card" style={{ width: '100%', maxWidth: '600px' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '20px', marginBottom: '30px' }}>
|
||||
<div style={{ width: '80px', height: '80px', borderRadius: '50%', background: 'linear-gradient(135deg, var(--accent-blue), var(--accent-green))', display: 'flex', justifyContent: 'center', alignItems: 'center', fontSize: '2.5rem', fontWeight: 'bold', color: 'white', boxShadow: 'var(--shadow-glow)' }}>
|
||||
{userProfile.display_name ? userProfile.display_name.charAt(0).toUpperCase() : '?'}
|
||||
</div>
|
||||
<div>
|
||||
<h2 style={{ margin: 0, fontSize: '1.8rem' }} className="text-blue">Profile Settings</h2>
|
||||
<p className="text-muted" style={{ margin: '5px 0 0 0' }}>Manage your personal details and security</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSave} style={{ display: 'flex', flexDirection: 'column', gap: '20px' }}>
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
||||
<label className="text-secondary" style={{ fontSize: '0.9rem', fontWeight: 600 }}>Username (Read Only)</label>
|
||||
<input
|
||||
type="text"
|
||||
value={userProfile.username}
|
||||
disabled
|
||||
style={{ padding: '12px', borderRadius: '8px', border: '1px solid var(--border-color)', background: 'rgba(0,0,0,0.2)', color: 'var(--text-muted)', cursor: 'not-allowed' }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '20px' }}>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
||||
<label className="text-secondary" style={{ fontSize: '0.9rem', fontWeight: 600 }}>Display Name</label>
|
||||
<input
|
||||
type="text" name="display_name" value={formData.display_name} onChange={handleChange}
|
||||
style={{ padding: '12px', borderRadius: '8px', border: '1px solid var(--border-color)', background: 'var(--bg-tertiary)', color: 'white' }}
|
||||
placeholder="Enter your name"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
||||
<label className="text-secondary" style={{ fontSize: '0.9rem', fontWeight: 600 }}>Email Address</label>
|
||||
<input
|
||||
type="email" name="email_id" value={formData.email_id} onChange={handleChange}
|
||||
style={{ padding: '12px', borderRadius: '8px', border: '1px solid var(--border-color)', background: 'var(--bg-tertiary)', color: 'white' }}
|
||||
placeholder="your.email@example.com"
|
||||
autoComplete="off"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
||||
<label className="text-secondary" style={{ fontSize: '0.9rem', fontWeight: 600 }}>Mobile Number</label>
|
||||
<input
|
||||
type="text" name="mobile_no" value={formData.mobile_no} onChange={handleChange}
|
||||
style={{ padding: '12px', borderRadius: '8px', border: '1px solid var(--border-color)', background: 'var(--bg-tertiary)', color: 'white' }}
|
||||
placeholder="+91 9999999999"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
||||
<label className="text-secondary" style={{ fontSize: '0.9rem', fontWeight: 600 }}>Gender</label>
|
||||
<select
|
||||
name="gender" value={formData.gender} onChange={handleChange}
|
||||
style={{ padding: '12px', borderRadius: '8px', border: '1px solid var(--border-color)', background: 'var(--bg-tertiary)', color: 'white' }}
|
||||
>
|
||||
<option value="">Select Gender</option>
|
||||
<option value="Male">Male</option>
|
||||
<option value="Female">Female</option>
|
||||
<option value="Other">Other</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ borderTop: '1px solid var(--border-color)', margin: '10px 0' }}></div>
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
||||
<label className="text-orange" style={{ fontSize: '0.9rem', fontWeight: 600 }}>Change Password (Optional)</label>
|
||||
<div style={{ position: 'relative', display: 'flex', alignItems: 'center' }}>
|
||||
<input
|
||||
type={showPassword ? "text" : "password"}
|
||||
name="password"
|
||||
value={formData.password}
|
||||
onChange={handleChange}
|
||||
style={{ width: '100%', padding: '12px', paddingRight: '45px', borderRadius: '8px', border: '1px solid rgba(245, 158, 11, 0.3)', background: 'var(--bg-tertiary)', color: 'white' }}
|
||||
placeholder="Leave blank to keep current password"
|
||||
autoComplete="new-password"
|
||||
/>
|
||||
<span
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
style={{ position: 'absolute', right: '15px', cursor: 'pointer', fontSize: '1.2rem', userSelect: 'none' }}
|
||||
title={showPassword ? "Hide password" : "Show password"}
|
||||
>
|
||||
{showPassword ? "🙈" : "👁️"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{message && <div style={{ padding: '10px', background: 'var(--accent-green-bg)', color: 'var(--accent-green)', borderRadius: '8px', border: '1px solid rgba(16, 185, 129, 0.2)' }}>{message}</div>}
|
||||
{error && <div style={{ padding: '10px', background: 'var(--accent-red-bg)', color: 'var(--accent-red)', borderRadius: '8px', border: '1px solid rgba(239, 68, 68, 0.2)' }}>{error}</div>}
|
||||
|
||||
<div style={{ display: 'flex', justifyContent: 'flex-end', marginTop: '10px' }}>
|
||||
<button type="submit" className="nav-btn active" style={{ padding: '12px 30px', fontSize: '1rem' }} disabled={loading}>
|
||||
{loading ? "Saving..." : "Save Changes"}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Profile;
|
||||
12
src/components/ProtectedRoute.js
Normal file
12
src/components/ProtectedRoute.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import React from "react";
|
||||
import { Navigate } from "react-router-dom";
|
||||
|
||||
function ProtectedRoute({ children }) {
|
||||
const token = localStorage.getItem("token");
|
||||
if (!token) {
|
||||
return <Navigate to="/login" replace />;
|
||||
}
|
||||
return children;
|
||||
}
|
||||
|
||||
export default ProtectedRoute;
|
||||
@@ -7,7 +7,7 @@ function Ribbon() {
|
||||
|
||||
const loadData = async () => {
|
||||
try {
|
||||
const res = await axios.get("http://127.0.0.1:8000/ribbon");
|
||||
const res = await axios.get("http://127.0.0.1:8000/api/scanner/ribbon");
|
||||
setData(res.data || []);
|
||||
setLoading(false);
|
||||
} catch (err) {
|
||||
|
||||
@@ -7,7 +7,7 @@ function Scanner({ onSelectStock, onSignal }) {
|
||||
|
||||
const loadData = async () => {
|
||||
try {
|
||||
const res = await axios.get("http://127.0.0.1:8000/scan");
|
||||
const res = await axios.get("http://127.0.0.1:8000/api/scanner/scan");
|
||||
const newData = res.data;
|
||||
|
||||
const best = newData.best_5 && newData.best_5.length > 0 ? newData.best_5[0] : null;
|
||||
|
||||
69
src/components/Signup.js
Normal file
69
src/components/Signup.js
Normal file
@@ -0,0 +1,69 @@
|
||||
import React, { useState } from "react";
|
||||
import axios from "axios";
|
||||
import { useNavigate, Link } from "react-router-dom";
|
||||
import forge from "node-forge";
|
||||
|
||||
function Signup() {
|
||||
const [username, setUsername] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [error, setError] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleSignup = async (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
setError("");
|
||||
try {
|
||||
const resKey = await axios.get("http://127.0.0.1:8000/api/auth/public-key");
|
||||
const publicKeyPem = resKey.data.public_key;
|
||||
const publicKey = forge.pki.publicKeyFromPem(publicKeyPem);
|
||||
const encrypted = publicKey.encrypt(password, 'RSA-OAEP');
|
||||
const encryptedBase64 = forge.util.encode64(encrypted);
|
||||
|
||||
await axios.post("http://127.0.0.1:8000/api/auth/signup", {
|
||||
username,
|
||||
password: encryptedBase64
|
||||
});
|
||||
|
||||
navigate("/login");
|
||||
} catch (err) {
|
||||
setError(err.response?.data?.detail || "Signup failed");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="app-container" style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh' }}>
|
||||
<div className="glass-card" style={{ width: '400px', textAlign: 'center' }}>
|
||||
<h2 className="text-green">✨ Sign Up</h2>
|
||||
<form onSubmit={handleSignup} style={{ display: 'flex', flexDirection: 'column', gap: '15px' }}>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Username"
|
||||
value={username}
|
||||
onChange={e => setUsername(e.target.value)}
|
||||
style={{ padding: '10px', borderRadius: '5px', border: '1px solid var(--border-color)', background: 'var(--bg-tertiary)', color: 'white' }}
|
||||
required
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
value={password}
|
||||
onChange={e => setPassword(e.target.value)}
|
||||
style={{ padding: '10px', borderRadius: '5px', border: '1px solid var(--border-color)', background: 'var(--bg-tertiary)', color: 'white' }}
|
||||
required
|
||||
/>
|
||||
{error && <p className="text-red">{error}</p>}
|
||||
<button type="submit" className="nav-btn active" style={{ justifyContent: 'center', background: 'var(--accent-green)', borderColor: 'var(--accent-green)' }} disabled={loading}>
|
||||
{loading ? "Signing up..." : "Sign Up"}
|
||||
</button>
|
||||
</form>
|
||||
<p style={{ marginTop: '20px' }}>Already have an account? <Link to="/login" className="text-blue">Login</Link></p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Signup;
|
||||
@@ -12,7 +12,7 @@ function SmartMoney() {
|
||||
|
||||
const loadData = async () => {
|
||||
try {
|
||||
const res = await axios.get("http://127.0.0.1:8000/smartmoney");
|
||||
const res = await axios.get("http://127.0.0.1:8000/api/scanner/smartmoney");
|
||||
setData(res.data);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
@@ -12,7 +12,7 @@ function TrendScanner() {
|
||||
|
||||
const loadData = async () => {
|
||||
try {
|
||||
const res = await axios.get("http://127.0.0.1:8000/trend");
|
||||
const res = await axios.get("http://127.0.0.1:8000/api/scanner/trend");
|
||||
setData(res.data);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
13
src/index.js
13
src/index.js
@@ -1,8 +1,17 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import App from "./App";
|
||||
import App from './App';
|
||||
import axios from 'axios';
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById("root"));
|
||||
axios.interceptors.request.use((config) => {
|
||||
const token = localStorage.getItem("token");
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
});
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
|
||||
Reference in New Issue
Block a user