Created
June 9, 2022 13:39
-
-
Save javierguzman/5ade008fa36f973f3dbe10c81a2e90cc to your computer and use it in GitHub Desktop.
Crazy Router
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const PrivateRoute: React.FC<PrivateRouteProps> = ({ | |
component: Component, | |
requiredPermission, | |
logout, | |
...rest | |
}) => { | |
const userIsLoggedIn = useAppSelector(state => isLoggedIn(state)); | |
const userRoles: string[] = useAppSelector(state => getRoles(state)); | |
const { t } = useTranslation('auth'); | |
const showPrivateRouteError = (): JSX.Element => { | |
if (!userIsLoggedIn && !logout) { | |
return <Redirect to="/login" />; | |
} else if (!userIsLoggedIn && logout){ | |
return <Redirect to="/" />; | |
}else { | |
return ( | |
<Container> | |
<Alert variant="danger" style={{ textAlign: 'center' }}> | |
{t('not-enough-permission')} | |
</Alert> | |
</Container> | |
); | |
} | |
}; | |
const userHasRequiredRoles = | |
!requiredPermission || | |
(userRoles && requiredPermission.some(requiredRole => userRoles.includes(requiredRole))); | |
return ( | |
<Route {...rest}> | |
{userIsLoggedIn && userHasRequiredRoles ? <Component /> : showPrivateRouteError()} | |
</Route> | |
); | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<PrivateRoute path="/profile/:userSlug/:section?" exact component={ViewProfile} /> | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import './ViewProfile.css'; | |
import React, { useState, useEffect } from 'react'; | |
import { Row, Col, Alert } from 'react-bootstrap'; | |
import { useHistory } from 'react-router-dom'; | |
import Head from '@Utils/Head'; | |
import { useTranslation } from 'react-i18next'; | |
import { useQuery, useQueryClient, QueryClient } from 'react-query'; | |
import { useAppSelector } from '@Store'; | |
import { getUserID, getUserSlug } from '@Auth/redux'; | |
import { UserSavedItinerariesDTOResponse } from '@Features/Itineraries/ItineraryDTO'; | |
import API from '@API'; | |
import { ShowPagination } from '@Features/ShowPagination'; | |
import LoaderWrapper from '@Loader'; | |
import { ShowItinerarySummary } from './ShowItinerarySummary'; | |
import { useQuery as useURLQuery } from '@Hooks/useQuery'; | |
const prefetchSavedItineraries = | |
(queryClient: QueryClient, userID: string) => | |
async (currentPage: number): Promise<void> => { | |
await queryClient.prefetchQuery(['user', 'savedItineraries', { userID, currentPage }], () => | |
fetchUserSavedItineraries(userID, currentPage) | |
); | |
}; | |
const fetchUserSavedItineraries = async (userID: string, currentPage: number): Promise<UserSavedItinerariesDTOResponse> => { | |
const api = API.getInstance(); | |
const getUserSavedItinerariesURL = `/api/profile/${userID}/itineraries`; | |
const response = await api.get(getUserSavedItinerariesURL, { | |
params: { | |
pageSize: 20, | |
offset: (currentPage -1) * 20 | |
} | |
}); | |
return response.data; | |
}; | |
const ShowProfileItineraries: React.FC<Record<string, never>> = () => { | |
const { t } = useTranslation('profile'); | |
const userID = useAppSelector(state => getUserID(state)); | |
const userSlug = useAppSelector(state => getUserSlug(state)); | |
const queryClient = useQueryClient(); | |
const history = useHistory(); | |
const urlQuery = useURLQuery(); | |
const queryCurrentPage = Number(urlQuery.get('page')); | |
const [currentPage, setCurrentPage] = useState(queryCurrentPage || 1); | |
useEffect(() => { | |
if (!queryCurrentPage || currentPage !== queryCurrentPage) { | |
history.replace(`/profile/${userSlug}/itineraries?page=${currentPage}`); | |
} | |
}, [currentPage, history, queryCurrentPage]); | |
const userSavedItinerariesInfo = useQuery(['user', 'savedItineraries', { userID, currentPage }], () => fetchUserSavedItineraries(userID, currentPage), { | |
refetchOnWindowFocus: false, | |
keepPreviousData: true, | |
retry: 2 | |
}); | |
const ShowTitle = ({ title }): JSX.Element => { | |
return <h1 className="profile-h1">{title}</h1>; | |
}; | |
const ShowUserItinerariesError = (): JSX.Element => { | |
let errorMessage = 'error'; | |
if (userSavedItinerariesInfo.error instanceof Error) { | |
errorMessage= userSavedItinerariesInfo.error?.message; | |
} | |
return ( | |
<Col md={12} | |
className="justify-content-center" | |
style={{ textAlign: 'center', marginTop: '3%' }} | |
> | |
<Alert variant="alert">{errorMessage}</Alert> | |
</Col> | |
); | |
}; | |
const ShowUserItineraries = (): JSX.Element => { | |
let isThereData = false; | |
if (userSavedItinerariesInfo?.data?.pagination?.totalNumberOfElements && userSavedItinerariesInfo.data.payload) { | |
isThereData = userSavedItinerariesInfo.data.pagination.totalNumberOfElements > 0 && userSavedItinerariesInfo.data.payload.length > 0; | |
} | |
if (isThereData) { | |
const itineraryList = userSavedItinerariesInfo?.data?.payload?.map(itineraryData => { | |
return ( | |
<> | |
<ShowItinerarySummary {...itineraryData} key={itineraryData.id} /> | |
<div style={{ marginTop: '30px' }}> | |
<ShowPagination | |
totalNumberOfElements={userSavedItinerariesInfo.data.pagination?.totalNumberOfElements || 20} | |
currentPage={currentPage} | |
setCurrentPage={setCurrentPage} | |
elementsPerPage={20} | |
className="kindoi-orange-pagination" | |
prefetchPage={prefetchSavedItineraries(queryClient, userID)} | |
/> | |
</div> | |
</> | |
); | |
}); | |
return <>{itineraryList}</>; | |
} else { | |
return ( | |
<Col md={12} | |
className="justify-content-center" | |
style={{ textAlign: 'center', marginTop: '3%' }} | |
> | |
<Alert variant="warning">{t('no-saved-itineraries')}</Alert> | |
</Col> | |
); | |
} | |
}; | |
const ShowUserItinerariesOrError = (): JSX.Element => { | |
if (userSavedItinerariesInfo.isError) { | |
return <ShowUserItinerariesError />; | |
} | |
return <ShowUserItineraries />; | |
}; | |
const ShowItinerariesSection = (): JSX.Element => { | |
const description = 'User saved itineraries'; | |
const title = 'User Itineraries'; | |
const keywords = 'user, profile, saved itineraries, Kindoi'; | |
return ( | |
<Row> | |
<Head title={title} description={description} keywords={keywords} /> | |
<Col md={{ span: 11, offset: 0 }} className="justify-content-center"> | |
<ShowTitle title={t('profile-itineraries-title')} /> | |
<LoaderWrapper width={80} height={500} isLoading={userSavedItinerariesInfo.isLoading || userSavedItinerariesInfo.isFetching}> | |
<Row> | |
<ShowUserItinerariesOrError /> | |
</Row> | |
</LoaderWrapper> | |
</Col> | |
</Row> | |
); | |
}; | |
return ( | |
<> | |
<ShowItinerariesSection /> | |
</> | |
); | |
}; | |
export { ShowProfileItineraries }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import './ViewProfile.css'; | |
import React, { useState, useEffect } from 'react'; | |
import { Container, Row, Col, Nav } from 'react-bootstrap'; | |
import { useTranslation } from 'react-i18next'; | |
import { NavLink, useParams } from 'react-router-dom'; | |
import { ShowProfileData } from './ShowProfileData'; | |
import { ShowProfileItineraries } from './ShowProfileItineraries'; | |
import { ShowDeleteAccount } from './ShowDeleteAccount'; | |
import AddressIcon from '@Assets/icons/address-card.svg'; | |
import TrashIcon from '@Assets/icons/trash.svg'; | |
import ClipboardIcon from '@Assets/icons/clipboard-list.svg'; | |
enum ProfileMenu { | |
PROFILE_DATA = 'PROFILE_DATA', | |
SAVED_ITINERARIES = 'SAVED_ITINERARIES' | |
} | |
const ViewProfile: React.FC<void> = () => { | |
const { t } = useTranslation('profile'); | |
const [profileMenu, setProfileMenu] = useState<ProfileMenu>(ProfileMenu.PROFILE_DATA); | |
const { userSlug, section } = useParams<{ | |
userSlug: string; | |
section: string; | |
}>(); | |
const [showDeleteWindow, setShowDeleteWindow] = useState(false); | |
useEffect(() => { | |
if (section === 'itineraries') { | |
setProfileMenu(ProfileMenu.SAVED_ITINERARIES); | |
} else { | |
setProfileMenu(ProfileMenu.PROFILE_DATA); | |
} | |
}, [section]); | |
const ShowProfileNavbar = ({ children }): JSX.Element => { | |
return ( | |
<Row style={{ marginTop: '3%' }}> | |
<Col md={3} style={{ marginTop: '5%' }}> | |
<Nav className="flex-column"> | |
<Nav.Link | |
as={NavLink} | |
exact | |
to={`/profile/${userSlug || 'user'}`} | |
href={`/profile/${userSlug || 'user'}`} | |
className="profile-menu-item" | |
> | |
<span> | |
<AddressIcon className='profile-menu-icon' /> | |
<span> | |
{t('personal-data')} | |
</span> | |
</span> | |
</Nav.Link> | |
<Nav.Link | |
as={NavLink} | |
exact | |
to={`/profile/${userSlug || 'user'}/itineraries`} | |
href={`/profile/${userSlug || 'user'}/itineraries`} | |
className="profile-menu-item" | |
> | |
<span> | |
<ClipboardIcon className='profile-menu-icon' /> | |
<span> | |
{t('itineraries')} | |
</span> | |
</span> | |
</Nav.Link> | |
<Nav.Link className="profile-menu-item" onClick={() => setShowDeleteWindow(true)}> | |
<span> | |
<TrashIcon className='profile-menu-icon' /> | |
<span> | |
{t('delete-account')} | |
</span> | |
</span> | |
</Nav.Link> | |
</Nav> | |
</Col> | |
<Col md={9} style={{ textAlign: 'center' }}> | |
{children} | |
</Col> | |
</Row> | |
); | |
}; | |
return ( | |
<Container> | |
<ShowProfileNavbar> | |
{profileMenu === ProfileMenu.PROFILE_DATA ? <ShowProfileData /> : <></>} | |
{profileMenu === ProfileMenu.SAVED_ITINERARIES ? <ShowProfileItineraries /> : <></>} | |
</ShowProfileNavbar> | |
<ShowDeleteAccount | |
shouldShow={showDeleteWindow} | |
closeWindow={() => setShowDeleteWindow(false)} | |
/> | |
</Container> | |
); | |
}; | |
export default ViewProfile; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment