working header!

This commit is contained in:
2024-04-09 19:44:45 +03:00
parent 90f37bd595
commit 162f8b50df
13 changed files with 172 additions and 49 deletions

View File

@ -13,7 +13,8 @@
"antd": "^5.15.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-redux": "^9.1.0"
"react-redux": "^9.1.0",
"react-router-dom": "^6.22.3"
},
"devDependencies": {
"@rsbuild/core": "^0.5.4",
@ -424,6 +425,14 @@
}
}
},
"node_modules/@remix-run/router": {
"version": "1.15.3",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz",
"integrity": "sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@rsbuild/core": {
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/@rsbuild/core/-/core-0.5.4.tgz",
@ -4012,6 +4021,36 @@
"node": ">=0.10.0"
}
},
"node_modules/react-router": {
"version": "6.22.3",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz",
"integrity": "sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==",
"dependencies": {
"@remix-run/router": "1.15.3"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"react": ">=16.8"
}
},
"node_modules/react-router-dom": {
"version": "6.22.3",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.3.tgz",
"integrity": "sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==",
"dependencies": {
"@remix-run/router": "1.15.3",
"react-router": "6.22.3"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"react": ">=16.8",
"react-dom": ">=16.8"
}
},
"node_modules/redux": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",

View File

@ -13,7 +13,8 @@
"antd": "^5.15.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-redux": "^9.1.0"
"react-redux": "^9.1.0",
"react-router-dom": "^6.22.3"
},
"devDependencies": {
"@rsbuild/core": "^0.5.4",

View File

@ -11,7 +11,6 @@ body {
line-height: 1.1;
text-align: center;
flex-direction: column;
justify-content: center;
}
.content h1 {

View File

@ -6,6 +6,7 @@ import { theme } from "./config/style";
import { Provider } from "react-redux";
import { store } from "./config/store";
import { MessageInstance } from "antd/es/message/interface";
import AppRoutes from "./pages/AppRoutes";
export const MessageContext = createContext({} as MessageInstance);
@ -21,6 +22,7 @@ const App = () => {
<div className="content">
{contextHolder}
<HeaderComponent />
<AppRoutes />
</div>
</MessageContext.Provider>
</ConfigProvider>

View File

@ -17,17 +17,16 @@ const AuthModal = (props: {
const [loginForm] = Form.useForm();
const [registerForm] = Form.useForm();
const { data, refetch, isFetching, isError } = useGetUserQuery({});
useEffect(() => {
if (!isFetching && !isError) {
store.dispatch(updateUser(data));
}
}, [data, isFetching, useGetUserQuery]);
const [current, setCurrent] = useState("login");
const messageApi = useContext(MessageContext);
// const { data, refetch, isFetching, isError } = useGetUserQuery({});
// useEffect(() => {
// console.log(data);
// if (!isFetching && !isError) {
// store.dispatch(updateUser(data));
// }
// }, [data, isFetching, useGetUserQuery]);
const [loginUser, { isLoading: isLoggingIn }] = useLoginMutation();
const [registerUser, { isLoading: isRegistering }] = useRegisterMutation();
@ -41,6 +40,7 @@ const AuthModal = (props: {
.then((data) => {
store.dispatch(updateToken(data.access_token));
})
.then(() => refetch())
.then(() => props.setOpen(false))
.catch(() => messageApi.error("Login failed!"));
};

View File

@ -1,10 +1,10 @@
import { UserOutlined } from "@ant-design/icons";
import { Layout, Menu, MenuProps } from "antd";
import { LogoutOutlined, UserOutlined } from "@ant-design/icons";
import { Button, Layout, Menu, MenuProps, Tooltip } from "antd";
import React, { useEffect, useState } from "react";
import AuthModal from "./AuthModal";
import "./styles.css";
import { getLocalToken, store, updateUser } from "../config/store";
import { User, useGetUserQuery } from "../slice/AuthApi";
import { StorePrototype, logOut, store } from "../config/store";
import { useSelector } from "react-redux";
const { Header } = Layout;
@ -12,31 +12,43 @@ type NullableUser = { name: string | null; username: string } | null;
const HeaderComponent = () => {
const [authModalOpen, setAuthModalOpen] = useState(false);
store.dispatch(getLocalToken());
const user: NullableUser = store.getState().auth.user;
// useEffect(() => {
// console.log(data);
// if (!isFetching && !isError) {
// store.dispatch(updateUser(data));
// }
// }, [data, isFetching, useGetUserQuery]);
const user: NullableUser = useSelector(
(state: StorePrototype) => state.auth.user
);
console.log(store.getState());
// const userMenuItems: MenuProps["userMenuItems"] = [
// {
// label: <Button>Log out</Button>,
// key: "logout",
// icon: <LogoutOutlined />,
// onClick: () => store.dispatch(logOut()),
// },
// ]
const items: MenuProps["items"] = [
{
label: user ? user.username : "Log In",
label: user ? (
<Tooltip
title={
<Button onClick={() => store.dispatch(logOut())}>Log out</Button>
}
>
{user.username}
</Tooltip>
) : (
"Log In"
),
key: "login",
icon: <UserOutlined />,
onClick: () => setAuthModalOpen(true),
onClick: () => !user && setAuthModalOpen(true),
},
];
return (
<>
<AuthModal open={authModalOpen} setOpen={setAuthModalOpen} />
<Header style={{ display: "flex", alignItems: "center" }}>
<Header>
<div className="demo-logo" />
<Menu
theme="dark"

View File

@ -5,7 +5,6 @@ import { store, updateUser } from "../config/store";
const authProvider = () => {
const { data, isFetching, isError } = useGetUserQuery({});
useEffect(() => {
console.log(data);
if (!isFetching && !isError) {
store.dispatch(updateUser(data));
}

View File

@ -1,42 +1,37 @@
import { configureStore, createAction, createReducer } from "@reduxjs/toolkit";
import {
ReducerType,
configureStore,
createAction,
createReducer,
} from "@reduxjs/toolkit";
import { setupListeners } from "@reduxjs/toolkit/query";
import { AuthApi, User } from "../slice/AuthApi";
export type authState = {
export type AuthDataType = {
token: string | null;
user: { name: string | null; username: string } | null;
};
const initialAuthState: authState = {
const initialAuthDataState: AuthDataType = {
token: null,
user: null,
};
export type StorePrototype = {
AuthApi: ReducerType;
auth: AuthDataType;
};
export const updateToken = createAction<string>("auth/updateToken");
export const getLocalToken = createAction("auth/getLocalToken");
export const updateUser = createAction<User>("auth/updateUser");
const parseJwt = (token: string): { sub: string; exp: number } => {
const base64Url = token.split(".")[1];
const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
const jsonPayload = decodeURIComponent(
window
.atob(base64)
.split("")
.map(function (c) {
return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2);
})
.join("")
);
return JSON.parse(jsonPayload);
};
export const logOut = createAction("auth/logOut");
export const store = configureStore({
reducer: {
// Add the generated reducer as a specific top-level slice
[AuthApi.reducerPath]: AuthApi.reducer,
auth: createReducer(initialAuthState, (builder) => {
auth: createReducer(initialAuthDataState, (builder) => {
builder.addCase(updateToken, (state, action) => {
state.token = action.payload;
localStorage.setItem("token", action.payload);
@ -50,6 +45,11 @@ export const store = configureStore({
builder.addCase(updateUser, (state, action) => {
state.user = action.payload;
});
builder.addCase(logOut, (state) => {
localStorage.removeItem("token");
state.token = null;
state.user = null;
});
}),
},
// Adding the api middleware enables caching, invalidation, polling,

View File

@ -1,4 +1,6 @@
export const theme = {
import { ThemeConfig } from "antd";
export const theme: ThemeConfig = {
components: {
Modal: {
contentBg: "#001529",
@ -11,6 +13,10 @@ export const theme = {
},
Button: {
primaryColor: "#001529",
defaultHoverBg: "#001529",
defaultHoverColor: "white",
colorPrimaryBgHover: "#001529",
// colorPrimaryHover: "#001529",
},
Message: {
contentBg: "#001c36",

View File

@ -0,0 +1,19 @@
import React, { useEffect } from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import MainPage from "./MainPage";
import { useGetUserQuery } from "../slice/AuthApi";
import { getLocalToken, store, updateUser } from "../config/store";
const AppRoutes = () => {
store.dispatch(getLocalToken());
return (
<BrowserRouter>
<Routes>
<Route path="/" Component={MainPage} />
</Routes>
</BrowserRouter>
);
};
export default AppRoutes;

View File

@ -0,0 +1,19 @@
import React from "react";
import "./styles.css";
import logoSquare from "../../static/logo-square.png";
import { Button } from "antd";
const MainPage = () => {
return (
<div className="card main">
<img src={logoSquare} alt="logo" className="image" />
<p style={{ fontSize: "2rem" }}>Organizing queues were never so simple</p>
<div className="button-box">
<Button>Join a queue</Button>
<Button>Take a tour</Button>
</div>
</div>
);
};
export default MainPage;

View File

@ -0,0 +1,27 @@
.card {
background: #001529;
margin: 10px;
border-radius: 10px;
}
.main {
display: flex;
flex-flow: column;
justify-items: center;
align-items: center;
align-content: space-between;
}
.image {
height: 10vw;
width: auto;
}
.button-box {
width: 100%;
display: flex;
align-self: center;
flex-flow: row;
justify-items: center;
align-content: space-around;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB