Skip to content

Instantly share code, notes, and snippets.

@javierguzman
Created June 9, 2022 13:39
Show Gist options
  • Save javierguzman/5ade008fa36f973f3dbe10c81a2e90cc to your computer and use it in GitHub Desktop.
Save javierguzman/5ade008fa36f973f3dbe10c81a2e90cc to your computer and use it in GitHub Desktop.
Crazy Router
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>
);
};
<PrivateRoute path="/profile/:userSlug/:section?" exact component={ViewProfile} />
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 };
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