diff --git a/backend/Dockerfile.dev b/backend/Dockerfile.dev index 23fdb0c..bae36e1 100644 --- a/backend/Dockerfile.dev +++ b/backend/Dockerfile.dev @@ -8,4 +8,4 @@ RUN npm install COPY ./code . -CMD ["sh", "-c","npx prisma db push --force-reset && npx prisma studio & npm run start:dev"] +CMD ["sh", "-c","npx prisma studio & npm run start:dev"] diff --git a/backend/code/src/profile/dto/profile.dto.ts b/backend/code/src/profile/dto/profile.dto.ts index df8a02b..95718fe 100644 --- a/backend/code/src/profile/dto/profile.dto.ts +++ b/backend/code/src/profile/dto/profile.dto.ts @@ -14,7 +14,7 @@ export type NAME = { last: string; }; -type PICTURE = { +export type PICTURE = { thumbnail: string; medium: string; large: string; diff --git a/backend/code/src/profile/profile.service.ts b/backend/code/src/profile/profile.service.ts index 23e35c8..3033512 100644 --- a/backend/code/src/profile/profile.service.ts +++ b/backend/code/src/profile/profile.service.ts @@ -13,14 +13,16 @@ export class ProfileService { userId: string, friendId?: null | string, ): Promise { + let id = userId; if (friendId && friendId !== userId) { const blockid = [userId, friendId].sort().join('-'); const block = await this.usersService.getBlockbyId(blockid); if (block) { throw new HttpException('User not found', HttpStatus.NOT_FOUND); } + id = friendId; } - const user = await this.usersService.getUserById(userId); + const user = await this.usersService.getUserById(id); if (!user) { throw new HttpException('User not found', HttpStatus.NOT_FOUND); } diff --git a/backend/code/src/users/users.service.ts b/backend/code/src/users/users.service.ts index 7e688bd..23fc206 100644 --- a/backend/code/src/users/users.service.ts +++ b/backend/code/src/users/users.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from 'src/prisma/prisma.service'; import { CreateUserDto } from './dto/create-user.dto'; import { UpdateUserDto } from './dto/update-user.dto'; -import { NAME } from '../profile/dto/profile.dto'; +import { NAME, PICTURE } from '../profile/dto/profile.dto'; @Injectable() export class UsersService { @@ -135,7 +135,12 @@ export class UsersService { }); return users.map((user) => { const name: NAME = { first: user.firstName, last: user.lastName }; - return { name: name, id: user.userId, avatar: user.avatar }; + const avatar: PICTURE = { + thumbnail: `https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_48,w_48/${user.avatar}`, + medium: `https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_72,w_72/${user.avatar}`, + large: `https://res.cloudinary.com/trandandan/image/upload/c_thumb,h_128,w_128/${user.avatar}`, + }; + return { name , id: user.userId, avatar }; }); } } diff --git a/frontend/code/src/Api/base.tsx b/frontend/code/src/Api/base.tsx index b11ff40..6af97ec 100644 --- a/frontend/code/src/Api/base.tsx +++ b/frontend/code/src/Api/base.tsx @@ -1,5 +1,4 @@ import axios from 'axios'; -import toast from 'react-hot-toast'; const api = axios.create({ baseURL: `${process.env.REACT_APP_API_ENDPOINT}`, @@ -27,10 +26,7 @@ const errorHandler = async (error:any) => { refreshAttempted = false } } - else - { - toast.error(`${error.response.data.message}`) - } + return Promise.reject({ ...error }); }; diff --git a/frontend/code/src/Components/Chat/Services/ChatServices.ts b/frontend/code/src/Components/Chat/Services/ChatServices.ts index 8c98b10..85a8f03 100644 --- a/frontend/code/src/Components/Chat/Services/ChatServices.ts +++ b/frontend/code/src/Components/Chat/Services/ChatServices.ts @@ -1,4 +1,5 @@ -import api from "../../../Api/base"; +import api from "../../../Api/base"; + export const createNewRoomCall = async ( name: string, diff --git a/frontend/code/src/Components/FirstLogin/UploadAvatar.tsx b/frontend/code/src/Components/FirstLogin/UploadAvatar.tsx index 8e5d31c..1cacd18 100644 --- a/frontend/code/src/Components/FirstLogin/UploadAvatar.tsx +++ b/frontend/code/src/Components/FirstLogin/UploadAvatar.tsx @@ -1,32 +1,59 @@ import { useForm, SubmitHandler } from "react-hook-form" - - +import { useUserStore } from "../../Stores/stores" +import api from '../../Api/base' +import { toast } from "react-hot-toast" +import { useNavigate } from "react-router-dom" +import { UploadLogic } from './UploadLogic' type Inputs = { - Avatar: string - AvatarRequired: string + Avatar: string; + firstName : string ; + lastName : string ; + discreption : string; + email : string; } - - +const ERROR_MESSAGES = ["Field is required" , "Require min length of " , "Passed max length of"] +const payload_objects = ["firstName","lastName","email","phone","discreption"] +const data_names = ["First name", "Last name", "Email", "Phone", "Bio"]; export const UploadAvatar = () => { + const userStore = useUserStore(); + const navigate = useNavigate(); const { register, - handleSubmit, - formState: { errors }, - } = useForm() - const onSubmit: SubmitHandler = (data) => console.log(data) - + handleSubmit + } = useForm() + const onSubmit: SubmitHandler = async(data) => { + try{ + + toast.promise(api.post("/profile/me",{...data ,finishProfile: true }) , {loading:"Saving user information",success:"Saved successfully",error:"Error on Saving Data"}) + userStore.login(); + navigate("/Home") + }catch(e) + {} + } + const handleError = (errors : any) => { + //eslint-disable-next-line + payload_objects.map((item:any, index : number) =>{ + if (errors[`${item}`]?.type === "required") toast.error(`${data_names[index]} ${ERROR_MESSAGES[0]} `); + if (errors[`${item}`]?.type === "minLength") toast.error(`${data_names[index]} ${ERROR_MESSAGES[1]} 4`); + if (errors[`${item}`]?.type === "maxLength")toast.error(`${data_names[index]} ${ERROR_MESSAGES[2]} 50 `); + }) +} return ( -
- - - - - {errors.AvatarRequired && This field is required} - - - -
+ <> +
+ + + + + + + + + ) } \ No newline at end of file diff --git a/frontend/code/src/Components/FirstLogin/UploadLogic.tsx b/frontend/code/src/Components/FirstLogin/UploadLogic.tsx new file mode 100644 index 0000000..b25ae7a --- /dev/null +++ b/frontend/code/src/Components/FirstLogin/UploadLogic.tsx @@ -0,0 +1,47 @@ +import {useRef} from 'react' +import { Avatar } from '../Settings/assets/Avatar' +import { Edit } from '../Settings/assets/Edit' +import { useUserStore } from '../../Stores/stores' +import api from '../../Api/base' +import { toast } from 'react-hot-toast' +export const UploadLogic = () => { + const userStore = useUserStore(); + const inputRef = useRef(); + const handleUploadedFile = async(event:any) => { + const h_size = event.target.files[0].size / 1024; + if (h_size > 5120) + { + toast.error("uploded avatar is bigger than 5mb") + return Promise.reject("Error"); + } + const formData = new FormData(); + formData.append("image", event.target.files[0]); + + await api.post("/profile/avatar", formData ,{headers:{"Content-Type":"multipart/form-data"}}); + const res = await api.get("/profile/me") + userStore.setAvatar(res?.data?.picture); + + }; + const handleClick = () => { + inputRef?.current?.click(); + + } + return ( + <> +
+ +
+ +
+
+ { + toast.promise(handleUploadedFile(e),{ + loading: "Updating Profile Image", + success:"New Avatar Saved", + error:"Error On Uploading image" + },{position:"top-center",className:"h-20",duration:2000}) + }} ref={inputRef} + /> + + ) +} \ No newline at end of file diff --git a/frontend/code/src/Components/FirstLogin/index.tsx b/frontend/code/src/Components/FirstLogin/index.tsx index c7709df..502d257 100644 --- a/frontend/code/src/Components/FirstLogin/index.tsx +++ b/frontend/code/src/Components/FirstLogin/index.tsx @@ -1,16 +1,15 @@ import { UploadAvatar } from "./UploadAvatar"; + export const FirstLogin = () => { return ( <> -
+
-
+
-
- Avatar -
+
diff --git a/frontend/code/src/Components/Home/LeaderBoard.tsx b/frontend/code/src/Components/Home/LeaderBoard.tsx index a9e6348..c13f38f 100644 --- a/frontend/code/src/Components/Home/LeaderBoard.tsx +++ b/frontend/code/src/Components/Home/LeaderBoard.tsx @@ -2,7 +2,7 @@ import { Chart } from './assets/Chart' import { Table } from './assets/Table' export const LeaderBoard = () => { return ( -
+
Leader Board diff --git a/frontend/code/src/Components/Home/index.tsx b/frontend/code/src/Components/Home/index.tsx index a7f893f..de90d59 100644 --- a/frontend/code/src/Components/Home/index.tsx +++ b/frontend/code/src/Components/Home/index.tsx @@ -8,7 +8,7 @@ export const Home : FC = () : JSX.Element =>{ return ( <> -
+
leaderboard hero
-
+
diff --git a/frontend/code/src/Components/Layout/Assets/Search.tsx b/frontend/code/src/Components/Layout/Assets/Search.tsx index a3325cc..4f660f5 100644 --- a/frontend/code/src/Components/Layout/Assets/Search.tsx +++ b/frontend/code/src/Components/Layout/Assets/Search.tsx @@ -1,10 +1,36 @@ +import {useState , ChangeEvent , useEffect} from 'react' import {BiSearch} from 'react-icons/bi' +import { SearchResults } from './SearchResults' + +function useDebounce(value: T, delay?: number): T { + const [debouncedValue, setDebouncedValue] = useState(value) + + useEffect(() => { + const timer = setTimeout(() => setDebouncedValue(value), delay || 500) + + return () => { + clearTimeout(timer) + } + }, [value, delay]) + + return debouncedValue +} export const Search = () => { + const [searchText, setSearchText] = useState(""); + const DebounceValue = useDebounce(searchText); + const onSearchTextChange = (e: ChangeEvent) => setSearchText(e.target.value); + return ( -
- -
+ +
+ + {setSearchText("")}} onFocus={()=>{setSearchText("")}} value={searchText}/> + +
+
    + +
) } \ No newline at end of file diff --git a/frontend/code/src/Components/Layout/Assets/SearchResults.tsx b/frontend/code/src/Components/Layout/Assets/SearchResults.tsx new file mode 100644 index 0000000..7512b5c --- /dev/null +++ b/frontend/code/src/Components/Layout/Assets/SearchResults.tsx @@ -0,0 +1,40 @@ +import { useEffect, useState } from "react" +import api from "../../../Api/base" +import { Link } from "react-router-dom"; +export const SearchResults = (props:any) => { + const [result , setResult] = useState([]); + useEffect(() => + { + const search = async() => { + try { + const res = await api.get("/users/search",{params:{q:props.query}}) + setResult(res.data) + console.log(res.data) + } catch (error) { + } + } + props.query && search() + },[props.query]) + return ( +
+ { result.length > 0 && result.map((item : any , index : number) => { + return ( + +
  • +
    +
    +
    + +
    +
    + {item.name.first} {item.name.last} +
    +
  • + + ) + }) + } + {} +
    + ) +} \ No newline at end of file diff --git a/frontend/code/src/Components/Layout/index.tsx b/frontend/code/src/Components/Layout/index.tsx index 7aa0967..85c6139 100644 --- a/frontend/code/src/Components/Layout/index.tsx +++ b/frontend/code/src/Components/Layout/index.tsx @@ -13,8 +13,7 @@ import { Outlet } from "react-router"; import { matchRoutes, useLocation } from "react-router-dom"; import { useUserStore } from "../../Stores/stores"; import { useNavigate } from "react-router-dom"; -import api from "../../Api/base"; -// import { FirstLogin } from '../FirstLogin' +import { FirstLogin } from "../FirstLogin"; const routes = [ { path: "Profile/:id" }, @@ -25,11 +24,8 @@ const routes = [ { path: "Pure" }, { path: "Game" }, ]; -const HideBg = () => { - return ( -
    - ); -}; + + const useCurrentPath = () => { const location = useLocation(); const [{ route }]: any = matchRoutes(routes, location); @@ -43,16 +39,14 @@ export const Layout: FC = (): JSX.Element => { useLayoutEffect(() => { const log = async () => { try { - await user.login(); - } catch (e) { - try { - await api.get("/auth/refresh"); - await user.login(); - } catch (e) { + await user.login(); + } + catch(e){ navigate("/"); user.logout(); - } } + + }; log(); //eslint-disable-next-line @@ -61,14 +55,13 @@ export const Layout: FC = (): JSX.Element => { const obj = { x: "30", y: "20" }; return ( <> - {/* { - !user.profileComplet && - } */} - {user.isLogged && !user.profileComplet && } - {user.isLogged && ( + {user.profileComplet === false && user.isLogged ? ( + + ) : (
    @@ -77,7 +70,7 @@ export const Layout: FC = (): JSX.Element => {
    - +
    diff --git a/frontend/code/src/Components/Login/index.tsx b/frontend/code/src/Components/Login/index.tsx index e86c07c..eb0a9cd 100644 --- a/frontend/code/src/Components/Login/index.tsx +++ b/frontend/code/src/Components/Login/index.tsx @@ -1,6 +1,9 @@ import { Link } from "react-router-dom"; import pingpong from '../images/pingpong.svg' import {Button} from './Assets/Button' +import { useUserStore } from "../../Stores/stores"; +import { useNavigate } from "react-router-dom"; +import { useLayoutEffect } from "react"; // import { PageLoading } from '../Loading'; // const FallBackLoading = () => { // return ( @@ -11,7 +14,21 @@ import {Button} from './Assets/Button' // } export const Login = () => { - + const userStore = useUserStore(); + const navigate = useNavigate(); + useLayoutEffect(() => { + const check = async() => { + try { + const loggedin = await userStore.login(); + if (loggedin) + navigate("/home") + } catch (error) { + + } + } + check() + // eslint-disable-next-line + },[]) return ( <>
    diff --git a/frontend/code/src/Components/Profile/assets/Pong.tsx b/frontend/code/src/Components/Profile/assets/Pong.tsx index 362292c..25e3155 100644 --- a/frontend/code/src/Components/Profile/assets/Pong.tsx +++ b/frontend/code/src/Components/Profile/assets/Pong.tsx @@ -5,7 +5,7 @@ export const Pong = () => { - + diff --git a/frontend/code/src/Components/Profile/index.tsx b/frontend/code/src/Components/Profile/index.tsx index bab32a2..6bcf321 100644 --- a/frontend/code/src/Components/Profile/index.tsx +++ b/frontend/code/src/Components/Profile/index.tsx @@ -5,7 +5,7 @@ import { Message } from './assets/MessageB' import { History } from './History' import Hero from './assets/Hero.gif' import { useState , useEffect } from 'react' -import { Link, useParams } from 'react-router-dom' +import { Link, useNavigate, useParams } from 'react-router-dom' import { Load } from '../Loading/' import Newbie from '../Badges/Newbie.svg' import Master from '../Badges/Master.svg' @@ -13,30 +13,38 @@ import Ultimate from '../Badges/Ultimate.svg' import { Edit } from './assets/Edit' import { useUserStore } from '../../Stores/stores' import dots from './assets/svg/threedots.svg' +import api from '../../Api/base' +// import toast from 'react-hot-toast' export const Profile = () =>{ const user = useUserStore(); - const params = useParams() + const params = useParams(); + const navigate = useNavigate(); console.log(`params : ${params.id} type ${typeof(params.id)}`) const [users, setUsers] = useState(undefined); useEffect(() => { const fetchUser = async() => { - const response = await fetch(`https://randomuser.me/api?seed=${params.id}`) - const data = await response.json(); - const collecteduser = data.results[0] - setUsers(collecteduser) + try { + const res = await api.get(`profile/${params.id}`) + setUsers(res.data) + + } catch (error) { + navigate("/NotFound") + } } - if (params.id !== "me") + if (params.id !== user.id || params.id !== "me") fetchUser(); else setUsers(user) + //eslint-disable-next-line },[params, user]) - console.log(users) - - + // console.log(users) + // const handleSendReq = async() => { + // toast.promise(api.post("friend/add",{friendId:users.id}),{loading:`Sending friend request`,success:`request sent to ${users.name.first}`,error:"could not send friend request"}) + // } return ( <> -
    +
    bg hero @@ -58,9 +66,9 @@ export const Profile = () =>{ users?.name?.first ?
    {users?.name?.first} {users.name.last}
    : }
    - {user.bio} + {users?.bio}
    -
    +
    { params.id !== "me" ? @@ -72,7 +80,7 @@ export const Profile = () =>{ ) }
    -
    +
    newbie badge Master badge Ultimate badge diff --git a/frontend/code/src/Components/Settings/index.tsx b/frontend/code/src/Components/Settings/index.tsx index 5d6b48b..56932df 100644 --- a/frontend/code/src/Components/Settings/index.tsx +++ b/frontend/code/src/Components/Settings/index.tsx @@ -1,5 +1,4 @@ -import { Avatar } from "./assets/Avatar"; -import { Edit } from "./assets/Edit"; + import Newbie from "../Badges/Newbie.svg"; import Master from "../Badges/Master.svg"; import Ultimate from "../Badges/Ultimate.svg"; @@ -8,6 +7,7 @@ import toast from "react-hot-toast"; import { useState, useEffect } from "react"; import { Inputs } from "./assets/Inputs"; import { useUserStore } from "../../Stores/stores"; +import { UploadLogic } from "../FirstLogin/UploadLogic"; export const Setting = () => { const getdata: any = async () => { const data: any = await api.get("/test"); @@ -42,10 +42,7 @@ export const Setting = () => {
    - -
    - -
    +
    diff --git a/frontend/code/src/Stores/stores.ts b/frontend/code/src/Stores/stores.ts index bec3318..0250210 100644 --- a/frontend/code/src/Stores/stores.ts +++ b/frontend/code/src/Stores/stores.ts @@ -44,7 +44,7 @@ export type State = { type Action = { - login: () => Promise; + login: () => Promise; logout: () => void; toggleTfa: (tfa: State["tfa"]) => void; @@ -53,7 +53,7 @@ type Action = { updateEmail: (email: State["email"]) => void; updatePhone: (phone: State["phone"]) => void; updateBio: (bio: State["bio"]) => void; - + setAvatar : (picture : State['picture']) =>void; }; @@ -102,15 +102,19 @@ export const useUserStore = create()( })), updatePhone: (phone: State["phone"]) => set(() => ({ phone: phone })), updateBio: (bio: State["bio"]) => set(() => ({ bio: bio })), - + setAvatar : (picture : State['picture']) => set(() => ({picture:picture})), login: async () => { const res = await api.get("/profile/me"); - const user_data = res.data; - + var user_data = res.data; + // user_data.picture= null + const check = user_data.picture.large.split`/` + if (check[check.length - 1] === "null") + user_data.picture = null; const userInitialValue :State= { isLogged: true, id: user_data.id, - bio: "Default bio", + bio: user_data?.bio ?? "default bio", + phone: user_data.cell, name: { first: user_data.name.first, @@ -134,7 +138,7 @@ export const useUserStore = create()( }; // console.log(userInitialValue) set({ ...userInitialValue }); - return res.status + return userInitialValue.isLogged }, logout: () => { set({},true);