fixed login but design is bruh
This commit is contained in:
32
package-lock.json
generated
32
package-lock.json
generated
@ -8,9 +8,11 @@
|
|||||||
"name": "picrinth-admin",
|
"name": "picrinth-admin",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@material/material-color-utilities": "^0.3.0",
|
||||||
"react": "^19.1.1",
|
"react": "^19.1.1",
|
||||||
"react-dom": "^19.1.1",
|
"react-dom": "^19.1.1",
|
||||||
"react-router-dom": "^7.7.1"
|
"react-router-dom": "^7.7.1",
|
||||||
|
"react-toastify": "^11.0.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.32.0",
|
"@eslint/js": "^9.32.0",
|
||||||
@ -1023,6 +1025,12 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@material/material-color-utilities": {
|
||||||
|
"version": "0.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/material-color-utilities/-/material-color-utilities-0.3.0.tgz",
|
||||||
|
"integrity": "sha512-ztmtTd6xwnuh2/xu+Vb01btgV8SQWYCaK56CkRK8gEkWe5TuDyBcYJ0wgkMRn+2VcE9KUmhvkz+N9GHrqw/C0g==",
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
"node_modules/@nodelib/fs.scandir": {
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
@ -1894,6 +1902,15 @@
|
|||||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/clsx": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/color-convert": {
|
"node_modules/color-convert": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
@ -2947,6 +2964,19 @@
|
|||||||
"react-dom": ">=18"
|
"react-dom": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-toastify": {
|
||||||
|
"version": "11.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-11.0.5.tgz",
|
||||||
|
"integrity": "sha512-EpqHBGvnSTtHYhCPLxML05NLY2ZX0JURbAdNYa6BUkk+amz4wbKBQvoKQAB0ardvSarUBuY4Q4s1sluAzZwkmA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"clsx": "^2.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^18 || ^19",
|
||||||
|
"react-dom": "^18 || ^19"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/resolve-from": {
|
"node_modules/resolve-from": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||||
|
|||||||
@ -10,9 +10,11 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@material/material-color-utilities": "^0.3.0",
|
||||||
"react": "^19.1.1",
|
"react": "^19.1.1",
|
||||||
"react-dom": "^19.1.1",
|
"react-dom": "^19.1.1",
|
||||||
"react-router-dom": "^7.7.1"
|
"react-router-dom": "^7.7.1",
|
||||||
|
"react-toastify": "^11.0.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.32.0",
|
"@eslint/js": "^9.32.0",
|
||||||
|
|||||||
BIN
public/test.mp4
Normal file
BIN
public/test.mp4
Normal file
Binary file not shown.
35
src/App.tsx
35
src/App.tsx
@ -1,7 +1,14 @@
|
|||||||
import { BrowserRouter as Router, Routes, Route, Navigate, Outlet, useLocation } from 'react-router-dom';
|
import { BrowserRouter as Router, Routes, Route, Navigate, Outlet, useLocation } from 'react-router-dom';
|
||||||
import {useContext, useEffect, useState } from "react";
|
import {useEffect, useState } from "react";
|
||||||
|
|
||||||
import AuthContext, { AuthProvider } from "./auth/auth-provider"
|
import { ToastContainer } from "react-toastify";
|
||||||
|
import {
|
||||||
|
argbFromHex,
|
||||||
|
themeFromSourceColor,
|
||||||
|
applyTheme,
|
||||||
|
} from "@material/material-color-utilities";
|
||||||
|
|
||||||
|
import { AuthProvider } from "./auth/auth-provider"
|
||||||
import ping from "./auth/ping";
|
import ping from "./auth/ping";
|
||||||
|
|
||||||
import Login from "./pages/login/login"
|
import Login from "./pages/login/login"
|
||||||
@ -14,8 +21,22 @@ import useAuth from './auth/auth';
|
|||||||
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
const theme = themeFromSourceColor(argbFromHex("ffddaf"));
|
||||||
|
const systemDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
|
||||||
|
applyTheme(theme, { target: document.body, dark: systemDark });
|
||||||
return (
|
return (
|
||||||
<AuthProvider>
|
<AuthProvider>
|
||||||
|
<>
|
||||||
|
<ToastContainer
|
||||||
|
position="top-right"
|
||||||
|
autoClose={2000}
|
||||||
|
hideProgressBar={false}
|
||||||
|
newestOnTop={false}
|
||||||
|
closeOnClick={false}
|
||||||
|
rtl={false}
|
||||||
|
theme={"light"}
|
||||||
|
// transition={Bounce}
|
||||||
|
/>
|
||||||
<Router>
|
<Router>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/login" element={<Login />} />
|
<Route path="/login" element={<Login />} />
|
||||||
@ -30,13 +51,14 @@ function App() {
|
|||||||
<Route path="*" element={<div>404</div>} />
|
<Route path="*" element={<div>404</div>} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</Router>
|
</Router>
|
||||||
|
</>
|
||||||
</AuthProvider>
|
</AuthProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const PrivateRoute = () => {
|
const PrivateRoute = () => {
|
||||||
const { token } = useAuth();
|
const { token, loading } = useAuth();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const [checked, setChecked] = useState(false);
|
const [checked, setChecked] = useState(false);
|
||||||
const [isValid, setIsValid] = useState(false);
|
const [isValid, setIsValid] = useState(false);
|
||||||
@ -49,14 +71,15 @@ const PrivateRoute = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const result = await ping(token);
|
const result = await ping(token);
|
||||||
console.log(result)
|
|
||||||
setIsValid(result);
|
setIsValid(result);
|
||||||
setChecked(true);
|
setChecked(true);
|
||||||
};
|
};
|
||||||
|
if (!loading) {
|
||||||
checkAuth();
|
checkAuth();
|
||||||
}, [token]);
|
}
|
||||||
|
}, [token, loading]);
|
||||||
|
|
||||||
if (!checked) {
|
if (loading || !checked) {
|
||||||
return <div>Checking server availability...</div>;
|
return <div>Checking server availability...</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,40 +4,40 @@ import { type JSX, useEffect, useState, createContext } from "react"
|
|||||||
type AuthContextType = {
|
type AuthContextType = {
|
||||||
token: string | null;
|
token: string | null;
|
||||||
setToken: (token: string | null) => void;
|
setToken: (token: string | null) => void;
|
||||||
|
loading: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const AuthContext = createContext<AuthContextType>({
|
const AuthContext = createContext<AuthContextType>({
|
||||||
token: null,
|
token: null,
|
||||||
setToken: () => {},
|
setToken: () => {},
|
||||||
|
loading: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
export const AuthProvider = ({ children }: { children: JSX.Element }) => {
|
export const AuthProvider = ({ children }: { children: JSX.Element }) => {
|
||||||
const [token, setTokenState] = useState<string | null>(null);
|
const [token, setTokenState] = useState<string | null>(null);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const savedToken = localStorage.getItem("token");
|
const savedToken = localStorage.getItem("token");
|
||||||
if (savedToken) {
|
if (savedToken) {
|
||||||
setTokenState(savedToken);
|
setTokenState(savedToken);
|
||||||
console.log("meow")
|
|
||||||
}
|
}
|
||||||
console.log(savedToken)
|
setLoading(false)
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const setToken = (newToken: string | null) => {
|
const setToken = (newToken: string | null) => {
|
||||||
setTokenState(newToken);
|
setTokenState(newToken);
|
||||||
if (newToken) {
|
if (newToken) {
|
||||||
localStorage.setItem("token", newToken);
|
localStorage.setItem("token", newToken);
|
||||||
console.log("saved")
|
|
||||||
} else {
|
} else {
|
||||||
localStorage.removeItem("token");
|
localStorage.removeItem("token");
|
||||||
console.log("removed")
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AuthContext.Provider value={{ token, setToken }}>
|
<AuthContext.Provider value={{ token, setToken, loading }}>
|
||||||
{children}
|
{children}
|
||||||
</AuthContext.Provider>
|
</AuthContext.Provider>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useNavigate, useLocation } from "react-router-dom";
|
import { useNavigate, useLocation } from "react-router-dom";
|
||||||
|
|
||||||
|
import { toast } from "react-toastify"
|
||||||
|
|
||||||
import useAuth from "../../auth/auth";
|
import useAuth from "../../auth/auth";
|
||||||
|
|
||||||
|
|
||||||
@ -12,36 +15,52 @@ const Login = () => {
|
|||||||
|
|
||||||
const from = location.state?.from?.pathname || "/";
|
const from = location.state?.from?.pathname || "/";
|
||||||
|
|
||||||
const handleSubmit = (e: React.FormEvent) => {
|
const handleSubmit = () => {
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
// TODO: request to API
|
// TODO: request to API
|
||||||
if (username === "admin" && password === "1234") {
|
if ((username === "admin" && password === "1234") || (username === "a" && password === "a")) {
|
||||||
const token = "todo.jwt.token";
|
const token = "todo.jwt.token";
|
||||||
setToken(token);
|
setToken(token);
|
||||||
navigate(from, { replace: true });
|
navigate(from, { replace: true });
|
||||||
} else {
|
} else {
|
||||||
alert("Wrong login or password");
|
toast.error("Wrong login or password");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ padding: "20px" }}>
|
<div style={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "right",
|
||||||
|
alignItems: "center",
|
||||||
|
minHeight: "100vh",
|
||||||
|
flexDirection: "column",
|
||||||
|
gap: "15px",
|
||||||
|
margin: "0 15px"
|
||||||
|
}}>
|
||||||
<h2>Login</h2>
|
<h2>Login</h2>
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={(e) => {e.preventDefault(); handleSubmit()}}
|
||||||
|
id="form-id"
|
||||||
|
style={{
|
||||||
|
flexDirection: "column",
|
||||||
|
display: "flex",
|
||||||
|
gap: "5px",
|
||||||
|
width: "100%",
|
||||||
|
maxWidth: "400px",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div>
|
<div>
|
||||||
<label>Username:</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
placeholder=" Username"
|
||||||
value={username}
|
value={username}
|
||||||
onChange={(e) => setUsername(e.target.value)}
|
onChange={(e) => setUsername(e.target.value)}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label>Password:</label>
|
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
|
placeholder=" Password"
|
||||||
value={password}
|
value={password}
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
required
|
required
|
||||||
|
|||||||
Reference in New Issue
Block a user