diff --git a/README.md b/README.md index d2ea91a..0af44df 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,28 @@ ## A Forum platform for problem solving, knowledge sharing and community builders, join others for sharing knowledge. ### API Docs [Api Docs](https://learn-hub-backend-api.onrender.com/api/docs) + +## Requirements + +The following tools are required in order to start the installation. + +- Node +- [Node](https://nodejs.org/en/download/package-manager/) + +## Installation + +1. Clone this repository with `git clone https://github.com/chimobi-justice/learn-hub.git` +- Change directories into learn-hub +- cd learn-hub +2. Run `npm install` to install the Node dependencies +3. Create the .env file by duplicating the .env.example file +- VITE_API_BASE_URL +- if clone the backend [learn-hub-backend](https://github.com/chimobi-justice/learn-hub-backend.git) repo use the local server url or use the host backend url +- https://learn-hub-backend-api.onrender.com/api/v1 +4. Run the application +- npm run dev + + +You can now visit the app in your browser by visiting [http://localhost:5173](http://localhost:5173). + + diff --git a/src/App.tsx b/src/App.tsx index 36998b7..432698b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -25,10 +25,13 @@ import ArthoredArticles from '@pages/Users/AuthoredViews/Articles' import ShowArticle from '@pages/Articles/show' import CreateArticle from '@pages/Articles/create' +import ShowUserPublicPosts from '@pages/Users/show' + import Login from '@pages/Auth/Login' import Register from '@pages/Auth/Register' import PrivateRoute from './Route/privateRoute' +import AuthRoute from './Route/AuthRoute' const routes = createBrowserRouter( createRoutesFromElements( @@ -41,6 +44,8 @@ const routes = createBrowserRouter( } /> } /> + } /> + {/* private route */} } />} /> } />} /> @@ -54,8 +59,8 @@ const routes = createBrowserRouter( } />} /> {/* end private route */} - } /> - } /> + } />} /> + } />} /> } /> diff --git a/src/Route/AuthRoute.tsx b/src/Route/AuthRoute.tsx new file mode 100644 index 0000000..7bf3606 --- /dev/null +++ b/src/Route/AuthRoute.tsx @@ -0,0 +1,28 @@ +import { FunctionComponent, ReactElement, useEffect } from 'react' +import { useNavigate } from 'react-router-dom' + +import { useUser } from '@context/userContext' + +interface AuthRouteProps { + element: ReactElement; +} + +const AuthRoute: FunctionComponent = ({ element }) => { + const navigate = useNavigate(); + + const getToken = !!localStorage.getItem("ucType_"); + + const { user } = useUser(); + + useEffect(() => { + if (user && getToken) { + navigate('/', { replace: true}); + } + }, [user, getToken, navigate]); + + if (getToken) return null; + + return element; +} + +export default AuthRoute; \ No newline at end of file diff --git a/src/Route/privateRoute.tsx b/src/Route/privateRoute.tsx index c9ebb64..a2539fb 100644 --- a/src/Route/privateRoute.tsx +++ b/src/Route/privateRoute.tsx @@ -1,8 +1,6 @@ import { FunctionComponent, ReactElement } from 'react' import { Navigate, useLocation } from 'react-router-dom' -// import { useUser } from '@context/userContext' - interface PrivateRouteProps { element: ReactElement; } diff --git a/src/api/axiosInstance.ts b/src/api/axiosInstance.ts index c0ba157..3010d78 100644 --- a/src/api/axiosInstance.ts +++ b/src/api/axiosInstance.ts @@ -36,11 +36,8 @@ axiosInstance.interceptors.response.use( localStorage.removeItem('ucType_'); location.href = '/auth/login' } - throw new Error(error?.response?.data?.message - || error?.response?.status - || error?.message - || 'An unexpected error occurred'); + return Promise.reject(error); + } - return Promise.reject(new Error('An unexpected error occurred')); } ); diff --git a/src/api/endpoints/userEndpoints.ts b/src/api/endpoints/userEndpoints.ts index 1fd958d..b7169c2 100644 --- a/src/api/endpoints/userEndpoints.ts +++ b/src/api/endpoints/userEndpoints.ts @@ -9,3 +9,5 @@ export const UPLOAD_PROFILE_AVATAR_ENDPOINT = `${API_BASE_URL}/users/accounts/up export const UPDATE_PASSWORD_ENDPOINT = `${API_BASE_URL}/users/accounts/update-password`; export const DELETE_ACCOUNT_ENDPOINT = `${API_BASE_URL}/users/accounts/delete`; + +export const PUBLIC_USER_ENDPOINT = `${API_BASE_URL}/users`; diff --git a/src/api/types/messageResponse.ts b/src/api/types/messageResponse.ts index 9f00826..302e1f4 100644 --- a/src/api/types/messageResponse.ts +++ b/src/api/types/messageResponse.ts @@ -1,4 +1,3 @@ export interface MessageResponse { message: string; - // data: T } \ No newline at end of file diff --git a/src/api/types/user/index.ts b/src/api/types/user/index.ts index b2d6d99..e8f0069 100644 --- a/src/api/types/user/index.ts +++ b/src/api/types/user/index.ts @@ -1,16 +1,18 @@ export interface UserResponse { - id: string | number; - fullname: string; - email: string; - username: string; - twitter: string; - avatar: string; - gitHub: string; - website: string; - headlines: string; - state: string; - country: string; - bio: string; + data: { + id: string | number; + fullname: string; + email: string; + username: string; + twitter: string; + avatar: string; + gitHub: string; + website: string; + profile_headlines: string; + state: string; + country: string; + bio: string; + } } export interface UpdateProfileRequest { @@ -35,16 +37,8 @@ export interface UpdateProfileAvatarRequest { avatar: string; } -export interface UpdateProfileAvatarResponse { - message: string; -} - export interface UpdatePasswordRequest { current_password: string; password: string; password_confirmation: string; } - -export interface UpdatePasswordResponse { - message: string; -} diff --git a/src/components/ArticleCard/Articles.tsx b/src/components/ArticleCard/Articles.tsx index 4586368..2db3070 100644 --- a/src/components/ArticleCard/Articles.tsx +++ b/src/components/ArticleCard/Articles.tsx @@ -14,7 +14,7 @@ import { CiEdit } from 'react-icons/ci' import { MdDeleteOutline } from 'react-icons/md' import truncate from '@helpers/truncate' -import { stripTags } from '@helpers/stripTags'; +import { stripTags } from '@helpers/stripTags' interface IProps { articleImg: string; @@ -63,9 +63,9 @@ const ArticlesCard: FunctionComponent = ({ /> - = ({ fontSize={"14px"} lineHeight={"1.7em"} color={"#0009"} - dangerouslySetInnerHTML={stripTags(truncate(description, 250))} + dangerouslySetInnerHTML={stripTags(truncate(description, 200))} /> - - {authorUsername && authorAvatar && ( - - - - )} - - + + {authorUsername && ( + + + + )} + {authorUsername && ( {authorUsername} )} + - - {authorProfileHeadlines && ( - {truncate(authorProfileHeadlines, 60)} - )} + + {authorProfileHeadlines && ( + {truncate(authorProfileHeadlines, 60)} + )} - {read_time} - + {read_time} - - + {isOwner && ( diff --git a/src/components/Footer/index.tsx b/src/components/Footer/index.tsx index f12efa7..9fc7b1c 100644 --- a/src/components/Footer/index.tsx +++ b/src/components/Footer/index.tsx @@ -10,6 +10,7 @@ import { } from '@chakra-ui/react' import { FaXTwitter } from 'react-icons/fa6' import { IoLogoLinkedin } from 'react-icons/io' +import { FaGithub } from "react-icons/fa"; import { colors } from '../../colors' @@ -122,6 +123,17 @@ const Footer: FunctionComponent = () => { LinkedIn + + + Github + + diff --git a/src/components/HeroSection/index.tsx b/src/components/HeroSection/index.tsx index 50b7e5f..02f6993 100644 --- a/src/components/HeroSection/index.tsx +++ b/src/components/HeroSection/index.tsx @@ -99,6 +99,7 @@ const HeroSection: FunctionComponent = () => { { +const NavBarLg: FunctionComponent = () => { const { user } = useUser(); const { signOutMutation } = useSignOut() @@ -43,16 +43,20 @@ const NavBarLg: FunctionComponent = () => { justifyContent="space-between" py={"20px"} > - - - Learn Hub - - + + + + Learn Hub + + + + + { { + {Menu?.map((menu) => ( @@ -145,6 +146,7 @@ const NavBarSm: FunctionComponent = () => { = ({ author, createdAt }) => ( - - - + + + - By: {author.fullname} + By: {truncate(author?.fullname, 20)} posted {createdAt} diff --git a/src/components/index.tsx b/src/components/index.tsx index 380b396..16d8c2a 100644 --- a/src/components/index.tsx +++ b/src/components/index.tsx @@ -16,6 +16,7 @@ import TextArea from '@components/TextArea' import EmptyState from '@components/EmptyState' import Editor from '@components/Editor' import ContentBlockContent from '@components/CodeBlockContent' +import ThreadCard from '@components/ThreadCard' export { Alert, @@ -36,4 +37,5 @@ export { EmptyState, Editor, ContentBlockContent, + ThreadCard, } \ No newline at end of file diff --git a/src/constant/Code.ts b/src/constant/Code.ts deleted file mode 100644 index e3c8ea1..0000000 --- a/src/constant/Code.ts +++ /dev/null @@ -1,72 +0,0 @@ -export const CodeHighlight = ` - Creating a reusable Chakra UI Button component - - import { FunctionComponent, ReactElement, ReactNode } from 'react' - import { ButtonProps, Button as ChakraButton } from '@chakra-ui/react' - - const borderRadiusMap = { - lg: '15px', - md: '10px', - sm: '5px', - } - - type ButtonRoundedRadius = keyof typeof borderRadiusMap; - - type SizeType = 'sm' | 'md' | 'lg' | { base: string, sm?: string, md?: string, lg?: string }; - - type WidthSize = string | { base: string, sm?: string, md?: string, lg?: string }; - - interface IProps extends Pick { - isloading?: boolean; - isDisable?: boolean; - loadingText?: string; - size: SizeType; - width?: WidthSize; - variant: "outline" | "solid"; - type: "button" | "submit"; - rounded: ButtonRoundedRadius; - leftIcon?: ReactElement; - rightIcon?: ReactElement; - children: ReactNode; - onClick?: () => void; - } - - const Button: FunctionComponent = ({ - isloading, - isDisable, - loadingText, - size, - width, - variant = "solid", - type, - rounded = "sm", - leftIcon, - rightIcon, - onClick, - children, - ...rest - }) => { - const borderRadius = borderRadiusMap[rounded]; - - return ( - - {children} - - ) - } - - export default Button; -`; \ No newline at end of file diff --git a/src/constant/Menu.ts b/src/constant/Menu.ts index 56bc851..962cb98 100644 --- a/src/constant/Menu.ts +++ b/src/constant/Menu.ts @@ -14,4 +14,9 @@ export const Menu = [ name: "Articles", url: "articles" }, + { + id: 4, + name: "Discussions ~ coming soon", + url: "" + }, ]; \ No newline at end of file diff --git a/src/hooks/article/useGetAuthoredArticles.ts b/src/hooks/article/useGetAuthoredArticles.ts index 7f9ef7b..125bfb7 100644 --- a/src/hooks/article/useGetAuthoredArticles.ts +++ b/src/hooks/article/useGetAuthoredArticles.ts @@ -5,10 +5,17 @@ import { GET_ARTHORED_ARTICLES_ENDPOINT } from '@api/index' export const useGetAuthoredArticles = (limit: number = 0, username: string) => { const fetchPaginatedArticles = async ({ pageParam = 0 }) => { - const res = await axiosInstance.get(`${GET_ARTHORED_ARTICLES_ENDPOINT}/${username}?limit=${limit}&page=${pageParam}`) - - const dataResponse = await res.data; - return { ...dataResponse, prevOffset: pageParam }; + try { + const res = await axiosInstance.get(`${GET_ARTHORED_ARTICLES_ENDPOINT}/${username}?limit=${limit}&page=${pageParam}`); + const dataStatus = res.status; + const dataResponse = await res.data; + return { ...dataResponse, dataStatus, prevOffset: pageParam }; + } catch (error: any) { + if (error.response?.status === 404) { + return { dataStatus: 404 }; // Handle 404 by returning a custom response + } + throw error; // Rethrow any other error to be handled by React Query + } }; const { @@ -24,14 +31,23 @@ export const useGetAuthoredArticles = (limit: number = 0, username: string) => { initialPageParam: 1, placeholderData: keepPreviousData, getNextPageParam: (lastPage) => { - if (!lastPage.data.pagination.next_page_url) { + if (!lastPage?.data?.pagination?.next_page_url) { return undefined; } - return lastPage.data.pagination.current_page + 1 + return lastPage?.data?.pagination?.current_page + 1 } }) const articles = articlesResponse?.pages ?? null; + const dataStatus = articlesResponse?.pages?.[0]?.dataStatus ?? null; - return { articles, isLoading, isSuccess, fetchNextPage, hasNextPage, isFetchingNextPage } + return { + articles, + isLoading, + isSuccess, + fetchNextPage, + hasNextPage, + isFetchingNextPage, + dataStatus + } } \ No newline at end of file diff --git a/src/hooks/article/useGetPublicAuthoredArticles.ts b/src/hooks/article/useGetPublicAuthoredArticles.ts new file mode 100644 index 0000000..d98f464 --- /dev/null +++ b/src/hooks/article/useGetPublicAuthoredArticles.ts @@ -0,0 +1,54 @@ +import { keepPreviousData, useInfiniteQuery } from '@tanstack/react-query' + +import { axiosInstance } from '@api/axiosInstance' +import { GET_ARTHORED_ARTICLES_ENDPOINT } from '@api/index' + +export const useGetPublicAuthoredArticles = (limit: number = 0, username: string) => { + const fetchPublicAuthoredArticles = async ({ pageParam = 0 }) => { + try { + const res = await axiosInstance + .get(`${GET_ARTHORED_ARTICLES_ENDPOINT}/${username}/public?limit=${limit}&page=${pageParam}`); + const dataStatus = res.status; + const dataResponse = await res.data; + return { ...dataResponse, dataStatus, prevOffset: pageParam }; + } catch (error: any) { + if (error.response?.status === 404) { + return { dataStatus: 404 }; // Handle 404 by returning a custom response + } + throw error; // Rethrow any other error to be handled by React Query + } + }; + + const { + data: articlesResponse, + fetchNextPage, + hasNextPage, + isFetchingNextPage, + isLoading, + isSuccess + } = useInfiniteQuery({ + queryKey: ['public-author-articles', limit, username], + queryFn: fetchPublicAuthoredArticles, + placeholderData: keepPreviousData, + initialPageParam: 1, + getNextPageParam: (lastPage) => { + if (!lastPage?.data?.pagination?.next_page_url) { + return undefined; + } + return lastPage?.data?.pagination?.current_page + 1 + } + }) + + const articles = articlesResponse?.pages ?? null; + const dataStatus = articlesResponse?.pages?.[0]?.dataStatus ?? null; + + return { + articles, + isLoading, + isSuccess, + fetchNextPage, + hasNextPage, + isFetchingNextPage, + dataStatus + } +} \ No newline at end of file diff --git a/src/hooks/thread/useGetAuthoredThreads.ts b/src/hooks/thread/useGetAuthoredThreads.ts index b34dedc..3a2187d 100644 --- a/src/hooks/thread/useGetAuthoredThreads.ts +++ b/src/hooks/thread/useGetAuthoredThreads.ts @@ -5,10 +5,18 @@ import { GET_ARTHORED_THREADS_ENDPOINT } from '@api/index' export const useGetAuthoredThreads = (limit: number = 0, username: string) => { const fetchPaginatedArticles = async ({ pageParam = 0 }) => { - const res = await axiosInstance.get(`${GET_ARTHORED_THREADS_ENDPOINT}/${username}?limit=${limit}&page=${pageParam}`) - - const dataResponse = await res.data; - return { ...dataResponse, prevOffset: pageParam }; + try { + const res = await axiosInstance + .get(`${GET_ARTHORED_THREADS_ENDPOINT}/${username}?limit=${limit}&page=${pageParam}`); + const dataStatus = res.status; + const dataResponse = await res.data; + return { ...dataResponse, dataStatus, prevOffset: pageParam }; + } catch (error: any) { + if (error.response?.status === 404) { + return { dataStatus: 404 }; // Handle 404 by returning a custom response + } + throw error; // Rethrow any other error to be handled by React Query + } }; const { @@ -24,14 +32,23 @@ export const useGetAuthoredThreads = (limit: number = 0, username: string) => { initialPageParam: 1, placeholderData: keepPreviousData, getNextPageParam: (lastPage) => { - if (!lastPage.data.pagination.next_page_url) { + if (!lastPage?.data?.pagination?.next_page_url) { return undefined; } - return lastPage.data.pagination.current_page + 1 + return lastPage?.data?.pagination?.current_page + 1 } }) const threads = threadsResponse?.pages ?? null; + const dataStatus = threadsResponse?.pages?.[0]?.dataStatus ?? null; - return { threads, isLoading, isSuccess, fetchNextPage, hasNextPage, isFetchingNextPage } + return { + threads, + isLoading, + isSuccess, + fetchNextPage, + hasNextPage, + isFetchingNextPage, + dataStatus + } } \ No newline at end of file diff --git a/src/hooks/thread/useGetPublicAuthoredThreads.ts b/src/hooks/thread/useGetPublicAuthoredThreads.ts new file mode 100644 index 0000000..5a76167 --- /dev/null +++ b/src/hooks/thread/useGetPublicAuthoredThreads.ts @@ -0,0 +1,54 @@ +import { keepPreviousData, useInfiniteQuery } from '@tanstack/react-query' + +import { axiosInstance } from '@api/axiosInstance' +import { GET_ARTHORED_THREADS_ENDPOINT } from '@api/index' + +export const useGetPublicAuthoredThreads = (limit: number = 0, username: string) => { + const fetchPublicAuthoredThreads = async ({ pageParam = 0 }) => { + try { + const res = await axiosInstance + .get(`${GET_ARTHORED_THREADS_ENDPOINT}/${username}/public?limit=${limit}&page=${pageParam}`); + const dataStatus = res.status; + const dataResponse = await res.data; + return { ...dataResponse, dataStatus, prevOffset: pageParam }; + } catch (error: any) { + if (error.response?.status === 404) { + return { dataStatus: 404 }; // Handle 404 by returning a custom response + } + throw error; // Rethrow any other error to be handled by React Query + } + }; + + const { + data: threadsResponse, + fetchNextPage, + hasNextPage, + isFetchingNextPage, + isLoading, + isSuccess + } = useInfiniteQuery({ + queryKey: ['public-author-threads', limit, username], + queryFn: fetchPublicAuthoredThreads, + placeholderData: keepPreviousData, + initialPageParam: 1, + getNextPageParam: (lastPage) => { + if (!lastPage?.data?.pagination?.next_page_url) { + return undefined; + } + return lastPage?.data?.pagination?.current_page + 1 + } + }) + + const threads = threadsResponse?.pages ?? null; + const dataStatus = threadsResponse?.pages?.[0]?.dataStatus ?? null; + + return { + threads, + isLoading, + isSuccess, + fetchNextPage, + hasNextPage, + isFetchingNextPage, + dataStatus + } +} \ No newline at end of file diff --git a/src/hooks/user/useGetPublicUser.ts b/src/hooks/user/useGetPublicUser.ts new file mode 100644 index 0000000..c5fdc3d --- /dev/null +++ b/src/hooks/user/useGetPublicUser.ts @@ -0,0 +1,10 @@ +import { useQuery } from '@tanstack/react-query' + +import { getPublicUser } from '@services/user' + +export const useGetPublicUser = (username: string) => { + return useQuery({ + queryKey: ['user', username], + queryFn: () => getPublicUser(username), + }) +} \ No newline at end of file diff --git a/src/pages/Articles/HeroSection/index.tsx b/src/pages/Articles/HeroSection/index.tsx index 75e12be..63cc774 100644 --- a/src/pages/Articles/HeroSection/index.tsx +++ b/src/pages/Articles/HeroSection/index.tsx @@ -58,7 +58,7 @@ const ArticleHeroSection: FunctionComponent = ({ - + {authorName} diff --git a/src/pages/Articles/components/ArticleForm.tsx b/src/pages/Articles/components/ArticleForm.tsx index 0dd7f1e..328034d 100644 --- a/src/pages/Articles/components/ArticleForm.tsx +++ b/src/pages/Articles/components/ArticleForm.tsx @@ -146,7 +146,7 @@ const ArticleForm: FunctionComponent = ({ /> - + = ({ /> - + + + )} + + + + + ) +} + +export default PublicUserArticles; \ No newline at end of file diff --git a/src/pages/Users/show/index.tsx b/src/pages/Users/show/index.tsx new file mode 100644 index 0000000..48a2d73 --- /dev/null +++ b/src/pages/Users/show/index.tsx @@ -0,0 +1,111 @@ +import { FunctionComponent } from 'react' +import { useParams } from 'react-router-dom' +import { + Avatar, + Box, + Container, + Heading, + Tab, + TabIndicator, + TabList, + TabPanel, + TabPanels, + Tabs, + Text +} from '@chakra-ui/react' + +import { colors } from '../../../colors' +import PublicUserArticles from '@pages/Users/show/articles' +import PublicUserThreads from '@pages/Users/show/thread' +import { useGetPublicUser } from '@hooks/user/useGetPublicUser' +import PublicUserAboutDetails from '@pages/Users/show/about' +import { NotFound, Skeleton } from '@components/index' +// import FollowCard from '@components/FollowCard' + +const ShowUserPublicPosts: FunctionComponent = () => { + const { username } = useParams(); + + const { data, isLoading } = useGetPublicUser(username!) + + if (!data) return ; + + if (isLoading) return + + const userData = data?.data; + + return ( + + + + + {userData?.fullname} + + + + Articles + Threads + About + + + + + + + + + + + {userData && ( + + )} + + + + + + + + + + {userData?.fullname} + {/* 0 Follower */} + + {userData?.profile_headlines} + + + {/* + + */} + + {/* + + */} + + + + ) +} + +export default ShowUserPublicPosts; \ No newline at end of file diff --git a/src/pages/Users/show/thread.tsx b/src/pages/Users/show/thread.tsx new file mode 100644 index 0000000..cba8a50 --- /dev/null +++ b/src/pages/Users/show/thread.tsx @@ -0,0 +1,61 @@ +import { FunctionComponent, Fragment } from 'react' +import { useParams } from 'react-router-dom' + +import { Button, Skeleton, ThreadCard } from '@components/index' +import { useGetPublicAuthoredThreads } from '@hooks/thread/useGetPublicAuthoredThreads' +import { Box, Heading } from '@chakra-ui/react' + +const PublicUserThreads: FunctionComponent = () => { + const { username } = useParams(); + + const { + threads, + isLoading, + isSuccess, + hasNextPage, + fetchNextPage, + isFetchingNextPage + } = useGetPublicAuthoredThreads(20, username!); + + return ( + <> + {isLoading && } + + {threads && isSuccess && threads?.map((page: any, pageIndex: number) => ( + + {page?.data?.threads?.map((thread: any, index: number) => ( + + ))} + + {page?.data?.threads?.length === 0 && ( + + This author don't have any threads yet + + )} + + ))} + + {hasNextPage && ( + + + + )} + + ) +} + +export default PublicUserThreads; \ No newline at end of file diff --git a/src/services/user/index.tsx b/src/services/user/index.tsx index 431c685..e2917d2 100644 --- a/src/services/user/index.tsx +++ b/src/services/user/index.tsx @@ -1,16 +1,15 @@ import { axiosInstance } from '@api/axiosInstance' import { DELETE_ACCOUNT_ENDPOINT, + MessageResponse, + PUBLIC_USER_ENDPOINT, UPDATE_PASSWORD_ENDPOINT, UPDATE_PROFILE_ENDPOINT, UPLOAD_PROFILE_AVATAR_ENDPOINT, USER_ENDPOINT, UpdatePasswordRequest, - UpdatePasswordResponse, UpdateProfileAvatarRequest, - UpdateProfileAvatarResponse, UpdateProfileRequest, - UpdateProfileResponse, UserResponse } from '@api/index' @@ -19,17 +18,17 @@ export const getUser = async (): Promise => { return response.data; } -export const updatePassword = async (data: UpdatePasswordRequest): Promise => { +export const updatePassword = async (data: UpdatePasswordRequest): Promise => { const response = await axiosInstance.patch(UPDATE_PASSWORD_ENDPOINT, data); return response.data; } -export const updateProfile = async (data: UpdateProfileRequest): Promise => { +export const updateProfile = async (data: UpdateProfileRequest): Promise => { const response = await axiosInstance.patch(UPDATE_PROFILE_ENDPOINT, data); return response.data; } -export const uploadProfileAvatar = async (data: UpdateProfileAvatarRequest): Promise => { +export const uploadProfileAvatar = async (data: UpdateProfileAvatarRequest): Promise => { const response = await axiosInstance.patch(UPLOAD_PROFILE_AVATAR_ENDPOINT, data); return response.data; } @@ -37,4 +36,9 @@ export const uploadProfileAvatar = async (data: UpdateProfileAvatarRequest): Pro export const deleteAccount = async () => { const response = await axiosInstance.delete(DELETE_ACCOUNT_ENDPOINT); return response.data; +} + +export const getPublicUser = async (username: string): Promise => { + const response = await axiosInstance.get(`${PUBLIC_USER_ENDPOINT}/${username}`); + return response.data; } \ No newline at end of file diff --git a/src/validations/updateProfile.ts b/src/validations/updateProfile.ts index 7cc1c5b..394792a 100644 --- a/src/validations/updateProfile.ts +++ b/src/validations/updateProfile.ts @@ -5,12 +5,6 @@ export const updateProfileValidationSchema = () => Yup.object({ .required("Required"), username: Yup.string() .required("Required"), - twitter: Yup.string() - .required("Required"), - gitHub: Yup.string() - .required("Required"), - website: Yup.string() - .required("Required"), profile_headlines: Yup.string() .required("Required"), state: Yup.string() @@ -18,5 +12,11 @@ export const updateProfileValidationSchema = () => Yup.object({ country: Yup.string() .required("Required"), bio: Yup.string() - .required("Required") + .required("Required"), + twitter: Yup.string() + .nullable("Required"), + gitHub: Yup.string() + .nullable("Required"), + website: Yup.string() + .nullable("Required") }); \ No newline at end of file