Skip to content

Commit

Permalink
Integration with REST endpoints finished
Browse files Browse the repository at this point in the history
  • Loading branch information
ptakpiotr committed Jun 1, 2024
1 parent c049b21 commit 2dffc25
Show file tree
Hide file tree
Showing 14 changed files with 275 additions and 110 deletions.
15 changes: 15 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
},
"dependencies": {
"@fluentui/react-components": "^9.47.0",
"@stomp/stompjs": "^7.0.0",
"@tanstack/react-query": "^5.28.9",
"axios": "^1.6.8",
"jwt-decode": "^4.0.0",
"linq": "^4.0.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
27 changes: 23 additions & 4 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import { Suspense, createContext, lazy, useState } from "react";
import { Suspense, createContext, lazy, useEffect, useState } from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import { FluentProvider, webLightTheme } from "@fluentui/react-components";
import Header from "./components/Header";
import Footer from "./components/Footer";
import { ISeasonContext, IUserContext, IUserState } from "./Types";
import {
IJwtResponsePayload,
ISeasonContext,
IUserContext,
IUserState,
} from "./Types";
import Loading from "./pages/Loading";
import { jwtDecode } from "jwt-decode";

const Home = lazy(() => import("./pages/Home"));
const Login = lazy(() => import("./pages/Login"));
Expand All @@ -27,10 +33,23 @@ function App() {
);

const [userState, setUserState] = useState<IUserState>({
isLoggedIn: true,
userId: "132",
isLoggedIn: false,
userId: "",
});

useEffect(() => {
const token = localStorage.getItem("token");

if (token) {
const decodedToken = jwtDecode<IJwtResponsePayload>(token);

setUserState({
isLoggedIn: true,
userId: decodedToken.userId?.toString(),
});
}
}, []);

return (
<FluentProvider theme={webLightTheme}>
<UserContext.Provider
Expand Down
8 changes: 6 additions & 2 deletions src/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,8 +235,7 @@ export interface ConstructorStanding {
}

export interface IPost {
postId: string;
authorName: string;
id: number;
content: string;
title: string;
photo: string;
Expand All @@ -252,3 +251,8 @@ export interface IAddPost {
export interface IGeneralResponse {
message: string;
}

export interface IJwtResponsePayload {
userId: number;
sub: string;
}
53 changes: 42 additions & 11 deletions src/components/AddPost.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,41 +17,72 @@ import {
Toaster,
} from "@fluentui/react-components";
import { useContext, useId, useState } from "react";
import { IAddPost } from "../Types";
import { IAddPost, IGeneralResponse } from "../Types";
import { UserContext } from "../App";
import { addPostSchema } from "../validation";
import { ValidationError } from "yup";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import axios from "axios";

interface IProps {
isOpen: boolean;
closeDialog: () => void;
}

function AddPost({ isOpen, closeDialog }: IProps) {
//TODO: useMutation
const { userState } = useContext(UserContext);

const [post, setPost] = useState<IAddPost>({
authorId: userState?.userId!,
content: "",
photo: "",
title: "",
});
const queryClient = useQueryClient();

const toasterId = useId();
const { dispatchToast } = useToastController(toasterId);
const notify = (error: string) =>
dispatchToast(
<Toast>
<ToastTitle>Validation error occured</ToastTitle>
<ToastBody subtitle="Correct the issues">{error}</ToastBody>
<ToastTitle>An error occured while fetching posts</ToastTitle>
<ToastBody subtitle="Contact the admin">{error}</ToastBody>
</Toast>,
{ intent: "error", timeout: 3000 }
);

const { mutateAsync } = useMutation({
mutationKey: ["add-post"],
mutationFn: (post: IAddPost) => {
const token = localStorage.getItem("token");

return axios.post<IGeneralResponse>(
`${import.meta.env.VITE_BACKEND_URL}/posts`,
{
...post,
userId: parseInt(post.authorId),
},
{
headers: {
Authorization: `Bearer ${token}`,
},
}
);
},
onError: (err) => {
notify(err.message);
},
onSuccess: async (_) => {
await queryClient.refetchQueries({
queryKey: ["posts"],
});
},
});

const [post, setPost] = useState<IAddPost>({
authorId: userState?.userId!,
content: "",
photo: "",
title: "",
});

const addPost = async () => {
try {
await addPostSchema.validate(post);
await mutateAsync(post);
closeDialog();
} catch (err) {
const validErr = err as ValidationError;
Expand Down
13 changes: 13 additions & 0 deletions src/components/SmallPost.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.smallPost {
padding: 2rem;
margin: 1rem;
max-width: 70vw;

@media (max-width: 768px) {
max-width: 80vw;
}

@media (max-width: 460px) {
max-width: 90vw;
}
}
12 changes: 4 additions & 8 deletions src/components/SmallPost.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,29 @@
import {
Body1,
Card,
CardFooter,
CardHeader,
CardPreview,
Image,
} from "@fluentui/react-components";
import { IPost } from "../Types";
import { useNavigate } from "react-router-dom";
import style from "./SmallPost.module.scss";

type Props = IPost;

function SmallPost({ postId, authorName, title, photo }: Props) {
function SmallPost({ id, title, photo }: Props) {
const navigate = useNavigate();
return (
<Card
onClick={() => {
navigate(`/post/${postId}`);
}}
style={{
padding: "2rem",
margin: "1rem",
navigate(`/post/${id}`);
}}
className={style.smallPost}
>
<CardHeader header={<Body1>{title}</Body1>} />
<CardPreview>
<Image src={photo} fit="cover" shape="rounded" />
</CardPreview>
<CardFooter>by {authorName}</CardFooter>
</Card>
);
}
Expand Down
8 changes: 7 additions & 1 deletion src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@ import App from "./App";
import "./styles.scss";

const root = createRoot(document.getElementById("root")!);
const queryClient = new QueryClient();
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 3600,
},
},
});

root.render(
<StrictMode>
Expand Down
50 changes: 47 additions & 3 deletions src/pages/Login.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
import { Input, Label, Button, Text } from "@fluentui/react-components";
import {
Input,
Label,
Button,
Text,
MessageBar,
} from "@fluentui/react-components";
import { ChangeEvent, useCallback, useContext, useState } from "react";
import { IGeneralResponse, LoginUser } from "../Types";
import {
IErrorState,
IGeneralResponse,
IJwtResponsePayload,
LoginUser,
} from "../Types";
import { useMutation } from "@tanstack/react-query";
import axios from "axios";
import { jwtDecode } from "jwt-decode";
import { useNavigate } from "react-router-dom";
import { UserContext } from "../App";

Expand Down Expand Up @@ -30,6 +42,11 @@ function Login() {
}));
}, []);

const [errorState, setErrorState] = useState<IErrorState>({
isError: false,
content: "",
});

const { mutateAsync } = useMutation({
mutationKey: ["login"],
mutationFn: (user: LoginUser) => {
Expand All @@ -41,12 +58,24 @@ function Login() {
}
);
},
onError: (err) => {
setErrorState({
isError: true,
content: err.message,
});
},
onSuccess: (data) => {
if (data.status === 200) {
const token = data.data.message;
localStorage.setItem("token", token);

const decodedToken = jwtDecode<IJwtResponsePayload>(token);

if (setUserState) {
setUserState({ isLoggedIn: true, userId: token });
setUserState({
isLoggedIn: true,
userId: decodedToken.userId?.toString(),
});
}
navigate("/");
}
Expand Down Expand Up @@ -86,6 +115,21 @@ function Login() {
Login
</Button>
</div>
{errorState.isError ? (
<MessageBar
intent="error"
style={{
cursor: "pointer",
}}
onClick={() => {
setErrorState({ isError: false, content: "" });
}}
>
{errorState.content}
</MessageBar>
) : (
<></>
)}
</main>
);
}
Expand Down
Loading

0 comments on commit 2dffc25

Please sign in to comment.