Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[N/A]: ensures session on first render #1176

Merged
merged 1 commit into from
Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 15 additions & 25 deletions client/src/hooks/profile/index.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,28 @@
import { useMemo } from 'react';
import { useQuery, useQueryClient, useMutation } from '@tanstack/react-query';
import { useSession } from 'next-auth/react';

import { apiRawServiceWithoutAuth, apiService } from 'services/api';
import { authService } from 'services/authentication';

import type { User, PasswordPayload, ErrorResponse, ProfilePayload } from 'types';
import type { UseQueryResult, UseQueryOptions } from '@tanstack/react-query';
import type { AxiosPromise } from 'axios';

type ResponseData = UseQueryResult<User>;
export function useProfile() {
const { data: session } = useSession();

const DEFAULT_QUERY_OPTIONS: UseQueryOptions<User> = {
retry: false,
keepPreviousData: true,
refetchOnWindowFocus: false,
};

export function useProfile(): ResponseData {
const fetchProfile = useQuery(
['profile'],
() =>
apiService
.request({
method: 'GET',
url: '/users/me',
})
.then((response) => {
if (response.status === 200) return response.data.data;
return response;
}),
DEFAULT_QUERY_OPTIONS,
return useQuery(['profile', session.accessToken], () =>
apiService
.request<{
data: User & {
id: string;
type: 'users';
};
}>({
method: 'GET',
url: '/users/me',
})
.then(({ data }) => data?.data),
);

return useMemo<ResponseData>((): ResponseData => fetchProfile, [fetchProfile]);
}

export function useUpdateProfile() {
Expand Down
13 changes: 13 additions & 0 deletions client/src/lib/react-query/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { QueryClient } from '@tanstack/react-query';

// todo: when we are able to use RSC, use cache's React
const getQueryClient = () =>
new QueryClient({
defaultOptions: {
queries: {
staleTime: 60 * 1000,
},
},
});

export default getQueryClient;
7 changes: 0 additions & 7 deletions client/src/pages/_document.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
import Document, { Html, Head, Main, NextScript } from 'next/document';

import type { DocumentContext, DocumentInitialProps } from 'next/document';

class MyDocument extends Document {
static async getInitialProps(ctx: DocumentContext): Promise<DocumentInitialProps> {
const initialProps = await Document.getInitialProps(ctx);
return { ...initialProps };
}

render(): JSX.Element {
return (
<Html className="h-full bg-gray-100">
Expand Down
10 changes: 9 additions & 1 deletion client/src/pages/analysis/chart.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { useMemo } from 'react';
import classNames from 'classnames';
import { dehydrate } from '@tanstack/react-query';

import { auth } from '@/pages/api/auth/[...nextauth]';
import { useAppSelector, useAppDispatch } from 'store/hooks';
import { setVisualizationMode } from 'store/features/analysis';
import { analysisFilters } from 'store/features/analysis/filters';
Expand All @@ -13,6 +15,7 @@ import AnalysisDynamicMetadata from 'containers/analysis-visualization/analysis-
import Loading from 'components/loading';
import TitleTemplate from 'utils/titleTemplate';
import { tasksSSR } from 'services/ssr';
import getQueryClient from '@/lib/react-query';

import type { ReactElement } from 'react';
import type { NextPageWithLayout } from 'pages/_app';
Expand Down Expand Up @@ -87,7 +90,12 @@ export const getServerSideProps: GetServerSideProps = async ({ req, res, query }
},
};
}
return { props: { query } };
const session = await auth(req, res);
const queryClient = getQueryClient();

queryClient.setQueryData(['profile', session.accessToken], session.user);

return { props: { query, session, dehydratedState: dehydrate(queryClient) } };
} catch (error) {
if (error.response.status === 401) {
return {
Expand Down
11 changes: 10 additions & 1 deletion client/src/pages/analysis/map.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { MapProvider } from 'react-map-gl/maplibre';
import { dehydrate } from '@tanstack/react-query';

import { auth } from '@/pages/api/auth/[...nextauth]';
import useEffectOnce from 'hooks/once';
import { setVisualizationMode } from 'store/features/analysis';
import { useAppDispatch } from 'store/hooks';
Expand All @@ -8,6 +10,7 @@ import AnalysisLayout from 'layouts/analysis';
import AnalysisMap from 'containers/analysis-visualization/analysis-map';
import TitleTemplate from 'utils/titleTemplate';
import { tasksSSR } from 'services/ssr';
import getQueryClient from '@/lib/react-query';

import type { NextPageWithLayout } from 'pages/_app';
import type { ReactElement } from 'react';
Expand Down Expand Up @@ -49,7 +52,13 @@ export const getServerSideProps: GetServerSideProps = async ({ req, res, query }
},
};
}
return { props: { query } };

const session = await auth(req, res);
const queryClient = getQueryClient();

queryClient.setQueryData(['profile', session.accessToken], session.user);

return { props: { query, session, dehydratedState: dehydrate(queryClient) } };
} catch (error) {
if (error.response.status === 401) {
return {
Expand Down
12 changes: 11 additions & 1 deletion client/src/pages/analysis/table.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { dehydrate } from '@tanstack/react-query';

import { auth } from '@/pages/api/auth/[...nextauth]';
import { useAppDispatch } from 'store/hooks';
import useEffectOnce from 'hooks/once';
import { setVisualizationMode } from 'store/features/analysis';
Expand All @@ -6,6 +9,7 @@ import AnalysisLayout from 'layouts/analysis';
import AnalysisTable from 'containers/analysis-visualization/analysis-table';
import TitleTemplate from 'utils/titleTemplate';
import { tasksSSR } from 'services/ssr';
import getQueryClient from '@/lib/react-query';

import type { NextPageWithLayout } from 'pages/_app';
import type { ReactElement } from 'react';
Expand Down Expand Up @@ -44,7 +48,13 @@ export const getServerSideProps: GetServerSideProps = async ({ req, res, query }
},
};
}
return { props: { query } };

const session = await auth(req, res);
const queryClient = getQueryClient();

queryClient.setQueryData(['profile', session.accessToken], session.user);

return { props: { query, session, dehydratedState: dehydrate(queryClient) } };
} catch (error) {
if (error.response.status === 401) {
return {
Expand Down
34 changes: 22 additions & 12 deletions client/src/pages/api/auth/[...nextauth].ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import NextAuth from 'next-auth';
import NextAuth, { getServerSession } from 'next-auth';
import CredentialsProvider from 'next-auth/providers/credentials';
import { GetServerSidePropsContext, NextApiRequest, NextApiResponse } from 'next';

import { authService } from 'services/authentication';
import getUserFullName from 'utils/user-full-name';
import { User } from '@/types';

import type { NextAuthOptions } from 'next-auth';

type CustomCredentials = Credential & {
password: string;
username: string;
};

export const options: NextAuthOptions = {
/**
* Defining custom pages
Expand Down Expand Up @@ -38,10 +35,13 @@ export const options: NextAuthOptions = {
password: { label: 'Password', type: 'password' },
},
async authorize(credentials) {
const { username, password } = credentials as CustomCredentials;
const { username, password } = credentials;

// Request to sign in
const signInRequest = await authService.request({
const signInRequest = await authService.request<{
user: User;
accessToken: string;
}>({
url: '/sign-in',
method: 'POST',
data: { username, password },
Expand All @@ -50,7 +50,6 @@ export const options: NextAuthOptions = {

const { data, status } = signInRequest;

// Parsing session data
if (data && status === 201) {
return {
...data.user,
Expand All @@ -71,8 +70,9 @@ export const options: NextAuthOptions = {
const newToken = { ...token };

if (user) {
const { accessToken } = user;
newToken.accessToken = accessToken as string;
const { accessToken, ...rest } = user;
newToken.accessToken = accessToken;
newToken.user = rest;

// If it's not expired, return the token,
// if (Date.now() / 1000 + TIME_TO_REFRESH_TOKEN < (newToken?.exp as number)) {
Expand All @@ -98,10 +98,20 @@ export const options: NextAuthOptions = {

// Extending session object
session({ session, token }) {
session.accessToken = token.accessToken as string;
session.accessToken = token.accessToken;
session.user = token.user;
return session;
},
},
};

export function auth(
...args:
| [GetServerSidePropsContext['req'], GetServerSidePropsContext['res']]
| [NextApiRequest, NextApiResponse]
| []
) {
return getServerSession(...args, options);
}

export default NextAuth(options);
18 changes: 18 additions & 0 deletions client/src/pages/data/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { useMemo } from 'react';
import Head from 'next/head';
import { GetServerSideProps } from 'next';
import { dehydrate } from '@tanstack/react-query';

import { auth } from '@/pages/api/auth/[...nextauth]';
import { useSourcingLocations } from 'hooks/sourcing-locations';
import { useLasTask } from 'hooks/tasks';
import AdminLayout from 'layouts/admin';
import AdminDataUploader from 'containers/admin/data-uploader';
import AdminDataTable from 'containers/admin/data-table';
import Loading from 'components/loading';
import Search from 'components/search';
import getQueryClient from '@/lib/react-query';

const AdminDataPage: React.FC = () => {
// Getting sourcing locations to check if there are any data
Expand Down Expand Up @@ -54,4 +58,18 @@ const AdminDataPage: React.FC = () => {
);
};

export const getServerSideProps: GetServerSideProps = async (ctx) => {
const session = await auth(ctx.req, ctx.res);
const queryClient = getQueryClient();

queryClient.setQueryData(['profile', session.accessToken], session.user);

return {
props: {
session,
dehydratedState: dehydrate(queryClient),
},
};
};

export default AdminDataPage;
19 changes: 18 additions & 1 deletion client/src/pages/data/scenarios/[scenarioId]/edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import toast from 'react-hot-toast';
import Head from 'next/head';
import Link from 'next/link';
import { useRouter } from 'next/router';
import { useQueryClient } from '@tanstack/react-query';
import { dehydrate, useQueryClient } from '@tanstack/react-query';
import { PlusIcon, DotsVerticalIcon } from '@heroicons/react/solid';
import { GetServerSideProps } from 'next';

import InfoTooltip from 'components/info-tooltip';
import { useScenario, useUpdateScenario } from 'hooks/scenarios';
Expand All @@ -26,6 +27,8 @@ import Toggle from 'components/toggle';
import Dropdown from 'components/dropdown';
import Badge from 'components/badge';
import { handleResponseError } from 'services/api';
import { auth } from '@/pages/api/auth/[...nextauth]';
import getQueryClient from '@/lib/react-query';

import type { ScenarioFormData } from 'containers/scenarios/types';

Expand Down Expand Up @@ -238,4 +241,18 @@ const UpdateScenarioPage: React.FC = () => {
);
};

export const getServerSideProps: GetServerSideProps = async (ctx) => {
const session = await auth(ctx.req, ctx.res);
const queryClient = getQueryClient();

queryClient.setQueryData(['profile', session.accessToken], session.user);

return {
props: {
session,
dehydratedState: dehydrate(queryClient),
},
};
};

export default UpdateScenarioPage;
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { useCallback } from 'react';
import Head from 'next/head';
import { useRouter } from 'next/router';
import toast from 'react-hot-toast';
import { GetServerSideProps } from 'next';
import { dehydrate } from '@tanstack/react-query';

import { useIntervention, useUpdateIntervention } from 'hooks/interventions';
import { parseInterventionFormDataToDto } from 'containers/interventions/utils';
Expand All @@ -10,6 +12,8 @@ import InterventionForm from 'containers/interventions/form';
import BackLink from 'components/back-link/component';
import Loading from 'components/loading';
import { handleResponseError } from 'services/api';
import { auth } from '@/pages/api/auth/[...nextauth]';
import getQueryClient from '@/lib/react-query';

import type { InterventionFormData } from 'containers/interventions/types';

Expand Down Expand Up @@ -86,4 +90,18 @@ const EditInterventionPage: React.FC = () => {
);
};

export const getServerSideProps: GetServerSideProps = async (ctx) => {
const session = await auth(ctx.req, ctx.res);
const queryClient = getQueryClient();

queryClient.setQueryData(['profile', session.accessToken], session.user);

return {
props: {
session,
dehydratedState: dehydrate(queryClient),
},
};
};

export default EditInterventionPage;
Loading
Loading