diff --git a/src/components/AuthenticationFlow/EmailLoginForm.tsx b/src/components/AuthenticationFlow/EmailLoginForm.tsx index c2c9f1525..a1065c490 100644 --- a/src/components/AuthenticationFlow/EmailLoginForm.tsx +++ b/src/components/AuthenticationFlow/EmailLoginForm.tsx @@ -16,7 +16,7 @@ const EmailLoginForm: FunctionComponent<{}> = () => { setIsLoading(true); setError(''); - const res = await fetch(`${import.meta.env.PUBLIC_API_URL}/v1-login`, { + fetch(`${import.meta.env.PUBLIC_API_URL}/v1-login`, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -25,27 +25,32 @@ const EmailLoginForm: FunctionComponent<{}> = () => { email, password, }), - }); + }) + .then((res) => res.json()) + .then((data) => { + if (data.type === 'user_not_verified') { + window.location.href = `/verification-pending?email=${encodeURIComponent( + email + )}`; + return; + } - const json = await res.json(); + if (!data.token) { + setIsLoading(false); + setError( + data.message || 'Something went wrong. Please try again later.' + ); + return; + } - if (json.type === 'user_not_verified') { - window.location.href = `/verification-pending?email=${encodeURIComponent( - email - )}`; - return; - } - - if (!json.token) { - setIsLoading(false); - setError(json.message || 'Something went wrong. Please try again later.'); - - return; - } - - // If the response is ok, we'll set the token in a cookie - Cookies.set(TOKEN_COOKIE_NAME, json.token); - window.location.href = '/'; + // If the response is ok, we'll set the token in a cookie + Cookies.set(TOKEN_COOKIE_NAME, data.token); + window.location.href = '/'; + }) + .catch((err) => { + setIsLoading(false); + setError('Something went wrong. Please try again later.'); + }); }; return ( @@ -86,7 +91,9 @@ const EmailLoginForm: FunctionComponent<{}> = () => {

- {error &&

{error}

} + {error && ( +

{error}

+ )} ); diff --git a/src/hooks/use-auth.ts b/src/hooks/use-auth.ts index a6bf4dd04..1b1395ac5 100644 --- a/src/hooks/use-auth.ts +++ b/src/hooks/use-auth.ts @@ -10,6 +10,7 @@ export const useAuth = () => { useEffect(() => { const token = Cookies.get(TOKEN_COOKIE_NAME); const payload = token ? decodeToken(token) : null; + setUser(payload); setIsLoading(false); }, []); diff --git a/src/lib/http.ts b/src/lib/http.ts new file mode 100644 index 000000000..74e9c319d --- /dev/null +++ b/src/lib/http.ts @@ -0,0 +1,124 @@ +import Cookies from 'js-cookie'; +import { TOKEN_COOKIE_NAME } from './constants'; + +type AppResponse = Record; + +type ApiReturn = { + response?: ResponseType; + error?: { + status: number; + message: string; + errors?: { message: string; location: string }[]; + }; +}; + +/** + * Wrapper around fetch to make it easy to handle errors + * + * @param url + * @param options + */ +export async function callApi( + url: string, + options?: RequestInit +): Promise> { + try { + const response = await fetch(url, { + credentials: 'include', + ...options, + headers: new Headers({ + 'Content-Type': 'application/json', + Accept: 'application/json', + Authorization: `Bearer ${Cookies.get(TOKEN_COOKIE_NAME)}`, + ...(options?.headers ?? {}), + }), + }); + + const data = await response.json(); + if (response.ok) { + return { + response: data as ResponseType, + error: undefined, + }; + } + + return { + response: undefined, + error: { + status: response.status, + message: data.message || 'Something went wrong. Please try again later.', + errors: data.errors, + }, + }; + } catch (error: any) { + return { + response: undefined, + error: { + status: 0, + message: error.message, + errors: [], + }, + }; + } +} + +export async function callPostApi( + url: string, + body: Record, + options?: RequestInit +): Promise> { + return callApi(url, { + ...options, + method: 'POST', + body: JSON.stringify(body), + }); +} + +export async function callGetApi( + url: string, + queryParams?: Record, + options?: RequestInit +): Promise> { + const searchParams = new URLSearchParams(queryParams).toString(); + const queryUrl = searchParams ? `${url}?${searchParams}` : url; + + return callApi(queryUrl, { + credentials: 'include', + method: 'GET', + ...options, + }); +} + +export async function callPatchApi( + url: string, + body: Record, + options?: RequestInit +): Promise> { + return callApi(url, { + ...options, + method: 'PATCH', + body: JSON.stringify(body), + }); +} + +export async function callPutApi( + url: string, + body: Record, + options?: RequestInit +): Promise> { + return callApi(url, { + ...options, + method: 'PUT', + body: JSON.stringify(body), + }); +} + +export async function callDeleteApi( + url: string, + options?: RequestInit +): Promise> { + return callApi(url, { + ...options, + method: 'DELETE', + }); +} diff --git a/src/pages/forgot-password.astro b/src/pages/forgot-password.astro index ffa5c4f0c..ea7e770a8 100644 --- a/src/pages/forgot-password.astro +++ b/src/pages/forgot-password.astro @@ -4,15 +4,26 @@ import SettingLayout from '../layouts/SettingLayout.astro'; --- -
- +
+
+
+

Forgot Password?

+

+ Enter your email address below and we will send you a link to reset your password. +

+
-
- Don't have an account? Sign up + + +
+ Don't have an account? Sign up +
diff --git a/src/pages/login.astro b/src/pages/login.astro index c3cf24977..b30370d2d 100644 --- a/src/pages/login.astro +++ b/src/pages/login.astro @@ -13,15 +13,10 @@ import SettingLayout from '../layouts/SettingLayout.astro'; noIndex={true} >
-
+

Login

- -

+

Welcome back! Let's take you to your account.