mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
fix: authentication redirection and UI (#4432)
* dev: update python version * dev: handle magic code attempt exhausted * dev: update app, space and god mode redirection paths * fix: handled signup and signin workflow * chore: auth input error indication and autofill styling improvement * dev: add app redirection urls * dev: update redirections * chore: onboarding improvement * chore: onboarding improvement * chore: redirection issue in space resolved * chore: instance empty state added * dev: fix app, space, admin redirection in docker setitngs --------- Co-authored-by: guru_sainath <gurusainath007@gmail.com> Co-authored-by: Anmol Singh Bhatia <anmolsinghbhatia@plane.so>
This commit is contained in:
parent
2d1201cc92
commit
88ebda42ff
@ -32,7 +32,7 @@ ENV NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_API_BASE_URL
|
|||||||
ARG NEXT_PUBLIC_WEB_BASE_URL=""
|
ARG NEXT_PUBLIC_WEB_BASE_URL=""
|
||||||
ENV NEXT_PUBLIC_WEB_BASE_URL=$NEXT_PUBLIC_WEB_BASE_URL
|
ENV NEXT_PUBLIC_WEB_BASE_URL=$NEXT_PUBLIC_WEB_BASE_URL
|
||||||
|
|
||||||
ARG NEXT_PUBLIC_SPACE_BASE_URL=""
|
ARG NEXT_PUBLIC_SPACE_BASE_URL="/spaces"
|
||||||
ENV NEXT_PUBLIC_SPACE_BASE_URL=$NEXT_PUBLIC_SPACE_BASE_URL
|
ENV NEXT_PUBLIC_SPACE_BASE_URL=$NEXT_PUBLIC_SPACE_BASE_URL
|
||||||
|
|
||||||
ARG NEXT_PUBLIC_ADMIN_BASE_PATH="/god-mode"
|
ARG NEXT_PUBLIC_ADMIN_BASE_PATH="/god-mode"
|
||||||
@ -62,7 +62,7 @@ ENV NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_API_BASE_URL
|
|||||||
ARG NEXT_PUBLIC_WEB_BASE_URL=""
|
ARG NEXT_PUBLIC_WEB_BASE_URL=""
|
||||||
ENV NEXT_PUBLIC_WEB_BASE_URL=$NEXT_PUBLIC_WEB_BASE_URL
|
ENV NEXT_PUBLIC_WEB_BASE_URL=$NEXT_PUBLIC_WEB_BASE_URL
|
||||||
|
|
||||||
ARG NEXT_PUBLIC_SPACE_BASE_URL=""
|
ARG NEXT_PUBLIC_SPACE_BASE_URL="/spaces"
|
||||||
ENV NEXT_PUBLIC_SPACE_BASE_URL=$NEXT_PUBLIC_SPACE_BASE_URL
|
ENV NEXT_PUBLIC_SPACE_BASE_URL=$NEXT_PUBLIC_SPACE_BASE_URL
|
||||||
|
|
||||||
ARG NEXT_PUBLIC_ADMIN_BASE_PATH="/god-mode"
|
ARG NEXT_PUBLIC_ADMIN_BASE_PATH="/god-mode"
|
||||||
|
46
admin/components/common/empty-state.tsx
Normal file
46
admin/components/common/empty-state.tsx
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import React from "react";
|
||||||
|
import Image from "next/image";
|
||||||
|
import { Button } from "@plane/ui";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
title: string;
|
||||||
|
description?: React.ReactNode;
|
||||||
|
image?: any;
|
||||||
|
primaryButton?: {
|
||||||
|
icon?: any;
|
||||||
|
text: string;
|
||||||
|
onClick: () => void;
|
||||||
|
};
|
||||||
|
secondaryButton?: React.ReactNode;
|
||||||
|
disabled?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const EmptyState: React.FC<Props> = ({
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
image,
|
||||||
|
primaryButton,
|
||||||
|
secondaryButton,
|
||||||
|
disabled = false,
|
||||||
|
}) => (
|
||||||
|
<div className={`flex h-full w-full items-center justify-center`}>
|
||||||
|
<div className="flex w-full flex-col items-center text-center">
|
||||||
|
{image && <Image src={image} className="w-52 sm:w-60" alt={primaryButton?.text || "button image"} />}
|
||||||
|
<h6 className="mb-3 mt-6 text-xl font-semibold sm:mt-8">{title}</h6>
|
||||||
|
{description && <p className="mb-7 px-5 text-custom-text-300 sm:mb-8">{description}</p>}
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
{primaryButton && (
|
||||||
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
prependIcon={primaryButton.icon}
|
||||||
|
onClick={primaryButton.onClick}
|
||||||
|
disabled={disabled}
|
||||||
|
>
|
||||||
|
{primaryButton.text}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{secondaryButton}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
@ -4,3 +4,4 @@ export * from "./controller-input";
|
|||||||
export * from "./copy-field";
|
export * from "./copy-field";
|
||||||
export * from "./password-strength-meter";
|
export * from "./password-strength-meter";
|
||||||
export * from "./banner";
|
export * from "./banner";
|
||||||
|
export * from "./empty-state";
|
||||||
|
@ -13,6 +13,7 @@ import { InstanceNotReady } from "@/components/instance";
|
|||||||
import { useInstance } from "@/hooks/store";
|
import { useInstance } from "@/hooks/store";
|
||||||
// helpers
|
// helpers
|
||||||
import { EInstancePageType } from "@/helpers";
|
import { EInstancePageType } from "@/helpers";
|
||||||
|
import { EmptyState } from "@/components/common";
|
||||||
|
|
||||||
type TInstanceWrapper = {
|
type TInstanceWrapper = {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
@ -41,7 +42,12 @@ export const InstanceWrapper: FC<TInstanceWrapper> = observer((props) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!instance) {
|
if (!instance) {
|
||||||
return <>Something went wrong</>;
|
return (
|
||||||
|
<EmptyState
|
||||||
|
title="Your instance wasn't configured successfully."
|
||||||
|
description="Please try re-installing Plane to fix the problem. If the issue still persists please reach out to support@plane.so."
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (instance?.instance?.is_setup_done === false && authEnabled === "1")
|
if (instance?.instance?.is_setup_done === false && authEnabled === "1")
|
||||||
|
@ -1,51 +1,52 @@
|
|||||||
AUTHENTICATION_ERROR_CODES = {
|
AUTHENTICATION_ERROR_CODES = {
|
||||||
# Global
|
# Global
|
||||||
"INSTANCE_NOT_CONFIGURED": 5000,
|
"INSTANCE_NOT_CONFIGURED": 5000,
|
||||||
"INVALID_EMAIL": 5012,
|
"INVALID_EMAIL": 5005,
|
||||||
"EMAIL_REQUIRED": 5013,
|
"EMAIL_REQUIRED": 5010,
|
||||||
"SIGNUP_DISABLED": 5001,
|
"SIGNUP_DISABLED": 5015,
|
||||||
# Password strength
|
# Password strength
|
||||||
"INVALID_PASSWORD": 5002,
|
"INVALID_PASSWORD": 5020,
|
||||||
"SMTP_NOT_CONFIGURED": 5007,
|
"SMTP_NOT_CONFIGURED": 5025,
|
||||||
# Sign Up
|
# Sign Up
|
||||||
"USER_ALREADY_EXIST": 5003,
|
"USER_ALREADY_EXIST": 5030,
|
||||||
"AUTHENTICATION_FAILED_SIGN_UP": 5006,
|
"AUTHENTICATION_FAILED_SIGN_UP": 5035,
|
||||||
"REQUIRED_EMAIL_PASSWORD_SIGN_UP": 5015,
|
"REQUIRED_EMAIL_PASSWORD_SIGN_UP": 5040,
|
||||||
"INVALID_EMAIL_SIGN_UP": 5017,
|
"INVALID_EMAIL_SIGN_UP": 5045,
|
||||||
"INVALID_EMAIL_MAGIC_SIGN_UP": 5019,
|
"INVALID_EMAIL_MAGIC_SIGN_UP": 5050,
|
||||||
"MAGIC_SIGN_UP_EMAIL_CODE_REQUIRED": 5023,
|
"MAGIC_SIGN_UP_EMAIL_CODE_REQUIRED": 5055,
|
||||||
# Sign In
|
# Sign In
|
||||||
"USER_DOES_NOT_EXIST": 5004,
|
"USER_DOES_NOT_EXIST": 5060,
|
||||||
"AUTHENTICATION_FAILED_SIGN_IN": 5005,
|
"AUTHENTICATION_FAILED_SIGN_IN": 5065,
|
||||||
"REQUIRED_EMAIL_PASSWORD_SIGN_IN": 5014,
|
"REQUIRED_EMAIL_PASSWORD_SIGN_IN": 5070,
|
||||||
"INVALID_EMAIL_SIGN_IN": 5016,
|
"INVALID_EMAIL_SIGN_IN": 5075,
|
||||||
"INVALID_EMAIL_MAGIC_SIGN_IN": 5018,
|
"INVALID_EMAIL_MAGIC_SIGN_IN": 5080,
|
||||||
"MAGIC_SIGN_IN_EMAIL_CODE_REQUIRED": 5022,
|
"MAGIC_SIGN_IN_EMAIL_CODE_REQUIRED": 5085,
|
||||||
# Both Sign in and Sign up
|
# Both Sign in and Sign up for magic
|
||||||
"INVALID_MAGIC_CODE": 5008,
|
"INVALID_MAGIC_CODE": 5090,
|
||||||
"EXPIRED_MAGIC_CODE": 5009,
|
"EXPIRED_MAGIC_CODE": 5095,
|
||||||
|
"EMAIL_CODE_ATTEMPT_EXHAUSTED": 5100,
|
||||||
# Oauth
|
# Oauth
|
||||||
"GOOGLE_NOT_CONFIGURED": 5010,
|
"GOOGLE_NOT_CONFIGURED": 5105,
|
||||||
"GITHUB_NOT_CONFIGURED": 5011,
|
"GITHUB_NOT_CONFIGURED": 5110,
|
||||||
"GOOGLE_OAUTH_PROVIDER_ERROR": 5021,
|
"GOOGLE_OAUTH_PROVIDER_ERROR": 5115,
|
||||||
"GITHUB_OAUTH_PROVIDER_ERROR": 5020,
|
"GITHUB_OAUTH_PROVIDER_ERROR": 5120,
|
||||||
# Reset Password
|
# Reset Password
|
||||||
"INVALID_PASSWORD_TOKEN": 5024,
|
"INVALID_PASSWORD_TOKEN": 5125,
|
||||||
"EXPIRED_PASSWORD_TOKEN": 5025,
|
"EXPIRED_PASSWORD_TOKEN": 5130,
|
||||||
# Change password
|
# Change password
|
||||||
"INCORRECT_OLD_PASSWORD": 5026,
|
"INCORRECT_OLD_PASSWORD": 5135,
|
||||||
"INVALID_NEW_PASSWORD": 5027,
|
"INVALID_NEW_PASSWORD": 5140,
|
||||||
# set passowrd
|
# set passowrd
|
||||||
"PASSWORD_ALREADY_SET": 5028,
|
"PASSWORD_ALREADY_SET": 5145,
|
||||||
# Admin
|
# Admin
|
||||||
"ADMIN_ALREADY_EXIST": 5029,
|
"ADMIN_ALREADY_EXIST": 5150,
|
||||||
"REQUIRED_ADMIN_EMAIL_PASSWORD_FIRST_NAME": 5030,
|
"REQUIRED_ADMIN_EMAIL_PASSWORD_FIRST_NAME": 5155,
|
||||||
"INVALID_ADMIN_EMAIL": 5031,
|
"INVALID_ADMIN_EMAIL": 5160,
|
||||||
"INVALID_ADMIN_PASSWORD": 5032,
|
"INVALID_ADMIN_PASSWORD": 5165,
|
||||||
"REQUIRED_ADMIN_EMAIL_PASSWORD": 5033,
|
"REQUIRED_ADMIN_EMAIL_PASSWORD": 5170,
|
||||||
"ADMIN_AUTHENTICATION_FAILED": 5034,
|
"ADMIN_AUTHENTICATION_FAILED": 5175,
|
||||||
"ADMIN_USER_ALREADY_EXIST": 5035,
|
"ADMIN_USER_ALREADY_EXIST": 5180,
|
||||||
"ADMIN_USER_DOES_NOT_EXIST": 5036,
|
"ADMIN_USER_DOES_NOT_EXIST": 5185,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -77,7 +77,13 @@ class MagicCodeProvider(CredentialAdapter):
|
|||||||
current_attempt = data["current_attempt"] + 1
|
current_attempt = data["current_attempt"] + 1
|
||||||
|
|
||||||
if data["current_attempt"] > 2:
|
if data["current_attempt"] > 2:
|
||||||
return key, ""
|
raise AuthenticationException(
|
||||||
|
error_code=AUTHENTICATION_ERROR_CODES[
|
||||||
|
"EMAIL_CODE_ATTEMPT_EXHAUSTED"
|
||||||
|
],
|
||||||
|
error_message="EMAIL_CODE_ATTEMPT_EXHAUSTED",
|
||||||
|
payload={"email": self.key},
|
||||||
|
)
|
||||||
|
|
||||||
value = {
|
value = {
|
||||||
"current_attempt": current_attempt,
|
"current_attempt": current_attempt,
|
||||||
|
@ -5,21 +5,38 @@ from urllib.parse import urlsplit
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
||||||
def base_host(request, is_admin=False, is_space=False):
|
def base_host(request, is_admin=False, is_space=False, is_app=False):
|
||||||
"""Utility function to return host / origin from the request"""
|
"""Utility function to return host / origin from the request"""
|
||||||
|
# Calculate the base origin from request
|
||||||
if is_admin and settings.ADMIN_BASE_URL:
|
base_origin = str(
|
||||||
return settings.ADMIN_BASE_URL
|
|
||||||
|
|
||||||
if is_space and settings.SPACE_BASE_URL:
|
|
||||||
return settings.SPACE_BASE_URL
|
|
||||||
|
|
||||||
return (
|
|
||||||
request.META.get("HTTP_ORIGIN")
|
request.META.get("HTTP_ORIGIN")
|
||||||
or f"{urlsplit(request.META.get('HTTP_REFERER')).scheme}://{urlsplit(request.META.get('HTTP_REFERER')).netloc}"
|
or f"{urlsplit(request.META.get('HTTP_REFERER')).scheme}://{urlsplit(request.META.get('HTTP_REFERER')).netloc}"
|
||||||
or f"""{"https" if request.is_secure() else "http"}://{request.get_host()}"""
|
or f"""{"https" if request.is_secure() else "http"}://{request.get_host()}"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Admin redirections
|
||||||
|
if is_admin:
|
||||||
|
if settings.ADMIN_BASE_URL:
|
||||||
|
return settings.ADMIN_BASE_URL
|
||||||
|
else:
|
||||||
|
return base_origin + "/god-mode/"
|
||||||
|
|
||||||
|
# Space redirections
|
||||||
|
if is_space:
|
||||||
|
if settings.SPACE_BASE_URL:
|
||||||
|
return settings.SPACE_BASE_URL
|
||||||
|
else:
|
||||||
|
return base_origin + "/spaces/"
|
||||||
|
|
||||||
|
# App Redirection
|
||||||
|
if is_app:
|
||||||
|
if settings.APP_BASE_URL:
|
||||||
|
return settings.APP_BASE_URL
|
||||||
|
else:
|
||||||
|
return base_origin
|
||||||
|
|
||||||
|
return base_origin
|
||||||
|
|
||||||
|
|
||||||
def user_ip(request):
|
def user_ip(request):
|
||||||
return str(request.META.get("REMOTE_ADDR"))
|
return str(request.META.get("REMOTE_ADDR"))
|
||||||
|
@ -5,12 +5,17 @@ from django.contrib.auth import login
|
|||||||
from plane.authentication.utils.host import base_host
|
from plane.authentication.utils.host import base_host
|
||||||
|
|
||||||
|
|
||||||
def user_login(request, user):
|
def user_login(request, user, is_app=False, is_admin=False, is_space=False):
|
||||||
login(request=request, user=user)
|
login(request=request, user=user)
|
||||||
device_info = {
|
device_info = {
|
||||||
"user_agent": request.META.get("HTTP_USER_AGENT", ""),
|
"user_agent": request.META.get("HTTP_USER_AGENT", ""),
|
||||||
"ip_address": request.META.get("REMOTE_ADDR", ""),
|
"ip_address": request.META.get("REMOTE_ADDR", ""),
|
||||||
"domain": base_host(request=request),
|
"domain": base_host(
|
||||||
|
request=request,
|
||||||
|
is_app=is_app,
|
||||||
|
is_admin=is_admin,
|
||||||
|
is_space=is_space,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
request.session["device_info"] = device_info
|
request.session["device_info"] = device_info
|
||||||
request.session.save()
|
request.session.save()
|
||||||
|
@ -42,8 +42,8 @@ class SignInAuthEndpoint(View):
|
|||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
# Base URL join
|
# Base URL join
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request),
|
base_host(request=request, is_app=True),
|
||||||
"accounts/sign-in?" + urlencode(params),
|
"sign-in?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@ -66,8 +66,8 @@ class SignInAuthEndpoint(View):
|
|||||||
if next_path:
|
if next_path:
|
||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request),
|
base_host(request=request, is_app=True),
|
||||||
"accounts/sign-in?" + urlencode(params),
|
"sign-in?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@ -85,8 +85,8 @@ class SignInAuthEndpoint(View):
|
|||||||
if next_path:
|
if next_path:
|
||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request),
|
base_host(request=request, is_app=True),
|
||||||
"accounts/sign-in?" + urlencode(params),
|
"sign-in?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@ -100,8 +100,8 @@ class SignInAuthEndpoint(View):
|
|||||||
if next_path:
|
if next_path:
|
||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request),
|
base_host(request=request, is_app=True),
|
||||||
"accounts/sign-in?" + urlencode(params),
|
"sign-in?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@ -111,7 +111,7 @@ class SignInAuthEndpoint(View):
|
|||||||
)
|
)
|
||||||
user = provider.authenticate()
|
user = provider.authenticate()
|
||||||
# Login the user and record his device info
|
# Login the user and record his device info
|
||||||
user_login(request=request, user=user)
|
user_login(request=request, user=user, is_app=True)
|
||||||
# Process workspace and project invitations
|
# Process workspace and project invitations
|
||||||
process_workspace_project_invitations(user=user)
|
process_workspace_project_invitations(user=user)
|
||||||
# Get the redirection path
|
# Get the redirection path
|
||||||
@ -121,15 +121,15 @@ class SignInAuthEndpoint(View):
|
|||||||
path = get_redirection_path(user=user)
|
path = get_redirection_path(user=user)
|
||||||
|
|
||||||
# redirect to referer path
|
# redirect to referer path
|
||||||
url = urljoin(base_host(request=request), path)
|
url = urljoin(base_host(request=request, is_app=True), path)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
except AuthenticationException as e:
|
except AuthenticationException as e:
|
||||||
params = e.get_error_dict()
|
params = e.get_error_dict()
|
||||||
if next_path:
|
if next_path:
|
||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request),
|
base_host(request=request, is_app=True),
|
||||||
"accounts/sign-in?" + urlencode(params),
|
"sign-in?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@ -152,7 +152,7 @@ class SignUpAuthEndpoint(View):
|
|||||||
if next_path:
|
if next_path:
|
||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request),
|
base_host(request=request, is_app=True),
|
||||||
"?" + urlencode(params),
|
"?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
@ -173,7 +173,7 @@ class SignUpAuthEndpoint(View):
|
|||||||
if next_path:
|
if next_path:
|
||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request),
|
base_host(request=request, is_app=True),
|
||||||
"?" + urlencode(params),
|
"?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
@ -192,7 +192,7 @@ class SignUpAuthEndpoint(View):
|
|||||||
if next_path:
|
if next_path:
|
||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request),
|
base_host(request=request, is_app=True),
|
||||||
"?" + urlencode(params),
|
"?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
@ -207,7 +207,7 @@ class SignUpAuthEndpoint(View):
|
|||||||
if next_path:
|
if next_path:
|
||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request),
|
base_host(request=request, is_app=True),
|
||||||
"?" + urlencode(params),
|
"?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
@ -218,7 +218,7 @@ class SignUpAuthEndpoint(View):
|
|||||||
)
|
)
|
||||||
user = provider.authenticate()
|
user = provider.authenticate()
|
||||||
# Login the user and record his device info
|
# Login the user and record his device info
|
||||||
user_login(request=request, user=user)
|
user_login(request=request, user=user, is_app=True)
|
||||||
# Process workspace and project invitations
|
# Process workspace and project invitations
|
||||||
process_workspace_project_invitations(user=user)
|
process_workspace_project_invitations(user=user)
|
||||||
# Get the redirection path
|
# Get the redirection path
|
||||||
@ -227,14 +227,14 @@ class SignUpAuthEndpoint(View):
|
|||||||
else:
|
else:
|
||||||
path = get_redirection_path(user=user)
|
path = get_redirection_path(user=user)
|
||||||
# redirect to referer path
|
# redirect to referer path
|
||||||
url = urljoin(base_host(request=request), path)
|
url = urljoin(base_host(request=request, is_app=True), path)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
except AuthenticationException as e:
|
except AuthenticationException as e:
|
||||||
params = e.get_error_dict()
|
params = e.get_error_dict()
|
||||||
if next_path:
|
if next_path:
|
||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request),
|
base_host(request=request, is_app=True),
|
||||||
"?" + urlencode(params),
|
"?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
@ -24,7 +24,7 @@ class GitHubOauthInitiateEndpoint(View):
|
|||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
# Get host and next path
|
# Get host and next path
|
||||||
request.session["host"] = base_host(request=request)
|
request.session["host"] = base_host(request=request, is_app=True)
|
||||||
next_path = request.GET.get("next_path")
|
next_path = request.GET.get("next_path")
|
||||||
if next_path:
|
if next_path:
|
||||||
request.session["next_path"] = str(next_path)
|
request.session["next_path"] = str(next_path)
|
||||||
@ -42,7 +42,7 @@ class GitHubOauthInitiateEndpoint(View):
|
|||||||
if next_path:
|
if next_path:
|
||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request),
|
base_host(request=request, is_app=True),
|
||||||
"?" + urlencode(params),
|
"?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
@ -57,7 +57,7 @@ class GitHubOauthInitiateEndpoint(View):
|
|||||||
if next_path:
|
if next_path:
|
||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request),
|
base_host(request=request, is_app=True),
|
||||||
"?" + urlencode(params),
|
"?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
@ -110,7 +110,7 @@ class GitHubCallbackEndpoint(View):
|
|||||||
)
|
)
|
||||||
user = provider.authenticate()
|
user = provider.authenticate()
|
||||||
# Login the user and record his device info
|
# Login the user and record his device info
|
||||||
user_login(request=request, user=user)
|
user_login(request=request, user=user, is_app=True)
|
||||||
# Process workspace and project invitations
|
# Process workspace and project invitations
|
||||||
process_workspace_project_invitations(user=user)
|
process_workspace_project_invitations(user=user)
|
||||||
# Get the redirection path
|
# Get the redirection path
|
||||||
|
@ -24,7 +24,7 @@ from plane.authentication.adapter.error import (
|
|||||||
|
|
||||||
class GoogleOauthInitiateEndpoint(View):
|
class GoogleOauthInitiateEndpoint(View):
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
request.session["host"] = base_host(request=request)
|
request.session["host"] = base_host(request=request, is_app=True)
|
||||||
next_path = request.GET.get("next_path")
|
next_path = request.GET.get("next_path")
|
||||||
if next_path:
|
if next_path:
|
||||||
request.session["next_path"] = str(next_path)
|
request.session["next_path"] = str(next_path)
|
||||||
@ -42,7 +42,7 @@ class GoogleOauthInitiateEndpoint(View):
|
|||||||
if next_path:
|
if next_path:
|
||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request),
|
base_host(request=request, is_app=True),
|
||||||
"?" + urlencode(params),
|
"?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
@ -58,7 +58,7 @@ class GoogleOauthInitiateEndpoint(View):
|
|||||||
if next_path:
|
if next_path:
|
||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request),
|
base_host(request=request, is_app=True),
|
||||||
"?" + urlencode(params),
|
"?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
@ -108,7 +108,7 @@ class GoogleCallbackEndpoint(View):
|
|||||||
)
|
)
|
||||||
user = provider.authenticate()
|
user = provider.authenticate()
|
||||||
# Login the user and record his device info
|
# Login the user and record his device info
|
||||||
user_login(request=request, user=user)
|
user_login(request=request, user=user, is_app=True)
|
||||||
# Process workspace and project invitations
|
# Process workspace and project invitations
|
||||||
process_workspace_project_invitations(user=user)
|
process_workspace_project_invitations(user=user)
|
||||||
# Get the redirection path
|
# Get the redirection path
|
||||||
|
@ -90,8 +90,8 @@ class MagicSignInEndpoint(View):
|
|||||||
if next_path:
|
if next_path:
|
||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request),
|
base_host(request=request, is_app=True),
|
||||||
"accounts/sign-in?" + urlencode(params),
|
"sign-in?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@ -104,8 +104,8 @@ class MagicSignInEndpoint(View):
|
|||||||
if next_path:
|
if next_path:
|
||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request),
|
base_host(request=request, is_app=True),
|
||||||
"accounts/sign-in?" + urlencode(params),
|
"sign-in?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@ -116,7 +116,7 @@ class MagicSignInEndpoint(View):
|
|||||||
user = provider.authenticate()
|
user = provider.authenticate()
|
||||||
profile = Profile.objects.get(user=user)
|
profile = Profile.objects.get(user=user)
|
||||||
# Login the user and record his device info
|
# Login the user and record his device info
|
||||||
user_login(request=request, user=user)
|
user_login(request=request, user=user, is_app=True)
|
||||||
# Process workspace and project invitations
|
# Process workspace and project invitations
|
||||||
process_workspace_project_invitations(user=user)
|
process_workspace_project_invitations(user=user)
|
||||||
if user.is_password_autoset and profile.is_onboarded:
|
if user.is_password_autoset and profile.is_onboarded:
|
||||||
@ -129,7 +129,7 @@ class MagicSignInEndpoint(View):
|
|||||||
else str(process_workspace_project_invitations(user=user))
|
else str(process_workspace_project_invitations(user=user))
|
||||||
)
|
)
|
||||||
# redirect to referer path
|
# redirect to referer path
|
||||||
url = urljoin(base_host(request=request), path)
|
url = urljoin(base_host(request=request, is_app=True), path)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
except AuthenticationException as e:
|
except AuthenticationException as e:
|
||||||
@ -137,8 +137,8 @@ class MagicSignInEndpoint(View):
|
|||||||
if next_path:
|
if next_path:
|
||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request),
|
base_host(request=request, is_app=True),
|
||||||
"accounts/sign-in?" + urlencode(params),
|
"sign-in?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@ -163,7 +163,7 @@ class MagicSignUpEndpoint(View):
|
|||||||
if next_path:
|
if next_path:
|
||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request),
|
base_host(request=request, is_app=True),
|
||||||
"?" + urlencode(params),
|
"?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
@ -177,7 +177,7 @@ class MagicSignUpEndpoint(View):
|
|||||||
if next_path:
|
if next_path:
|
||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request),
|
base_host(request=request, is_app=True),
|
||||||
"?" + urlencode(params),
|
"?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
@ -188,7 +188,7 @@ class MagicSignUpEndpoint(View):
|
|||||||
)
|
)
|
||||||
user = provider.authenticate()
|
user = provider.authenticate()
|
||||||
# Login the user and record his device info
|
# Login the user and record his device info
|
||||||
user_login(request=request, user=user)
|
user_login(request=request, user=user, is_app=True)
|
||||||
# Process workspace and project invitations
|
# Process workspace and project invitations
|
||||||
process_workspace_project_invitations(user=user)
|
process_workspace_project_invitations(user=user)
|
||||||
# Get the redirection path
|
# Get the redirection path
|
||||||
@ -197,7 +197,7 @@ class MagicSignUpEndpoint(View):
|
|||||||
else:
|
else:
|
||||||
path = get_redirection_path(user=user)
|
path = get_redirection_path(user=user)
|
||||||
# redirect to referer path
|
# redirect to referer path
|
||||||
url = urljoin(base_host(request=request), path)
|
url = urljoin(base_host(request=request, is_app=True), path)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
except AuthenticationException as e:
|
except AuthenticationException as e:
|
||||||
@ -205,7 +205,7 @@ class MagicSignUpEndpoint(View):
|
|||||||
if next_path:
|
if next_path:
|
||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request),
|
base_host(request=request, is_app=True),
|
||||||
"?" + urlencode(params),
|
"?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
@ -146,7 +146,7 @@ class ResetPasswordEndpoint(View):
|
|||||||
)
|
)
|
||||||
params = exc.get_error_dict()
|
params = exc.get_error_dict()
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request),
|
base_host(request=request, is_app=True),
|
||||||
"accounts/reset-password?" + urlencode(params),
|
"accounts/reset-password?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
@ -159,8 +159,9 @@ class ResetPasswordEndpoint(View):
|
|||||||
error_message="INVALID_PASSWORD",
|
error_message="INVALID_PASSWORD",
|
||||||
)
|
)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request),
|
base_host(request=request, is_app=True),
|
||||||
"?" + urlencode(exc.get_error_dict()),
|
"accounts/reset-password?"
|
||||||
|
+ urlencode(exc.get_error_dict()),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@ -172,7 +173,7 @@ class ResetPasswordEndpoint(View):
|
|||||||
error_message="INVALID_PASSWORD",
|
error_message="INVALID_PASSWORD",
|
||||||
)
|
)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request),
|
base_host(request=request, is_app=True),
|
||||||
"accounts/reset-password?"
|
"accounts/reset-password?"
|
||||||
+ urlencode(exc.get_error_dict()),
|
+ urlencode(exc.get_error_dict()),
|
||||||
)
|
)
|
||||||
@ -184,8 +185,8 @@ class ResetPasswordEndpoint(View):
|
|||||||
user.save()
|
user.save()
|
||||||
|
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request),
|
base_host(request=request, is_app=True),
|
||||||
"accounts/sign-in?" + urlencode({"success": True}),
|
"sign-in?" + urlencode({"success": True}),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
except DjangoUnicodeDecodeError:
|
except DjangoUnicodeDecodeError:
|
||||||
@ -196,7 +197,7 @@ class ResetPasswordEndpoint(View):
|
|||||||
error_message="EXPIRED_PASSWORD_TOKEN",
|
error_message="EXPIRED_PASSWORD_TOKEN",
|
||||||
)
|
)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request),
|
base_host(request=request, is_app=True),
|
||||||
"accounts/reset-password?" + urlencode(exc.get_error_dict()),
|
"accounts/reset-password?" + urlencode(exc.get_error_dict()),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Python imports
|
# Python imports
|
||||||
from urllib.parse import urlencode, urljoin
|
from urllib.parse import urljoin
|
||||||
|
|
||||||
# Django imports
|
# Django imports
|
||||||
from django.views import View
|
from django.views import View
|
||||||
@ -23,12 +23,9 @@ class SignOutAuthEndpoint(View):
|
|||||||
user.save()
|
user.save()
|
||||||
# Log the user out
|
# Log the user out
|
||||||
logout(request)
|
logout(request)
|
||||||
url = urljoin(
|
url = urljoin(base_host(request=request, is_app=True), "sign-in")
|
||||||
base_host(request=request),
|
|
||||||
"accounts/sign-in?" + urlencode({"success": "true"}),
|
|
||||||
)
|
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
except Exception:
|
except Exception:
|
||||||
return HttpResponseRedirect(
|
return HttpResponseRedirect(
|
||||||
base_host(request=request), "accounts/sign-in"
|
base_host(request=request, is_app=True), "sign-in"
|
||||||
)
|
)
|
||||||
|
@ -70,7 +70,7 @@ class ChangePasswordEndpoint(APIView):
|
|||||||
user.set_password(serializer.data.get("new_password"))
|
user.set_password(serializer.data.get("new_password"))
|
||||||
user.is_password_autoset = False
|
user.is_password_autoset = False
|
||||||
user.save()
|
user.save()
|
||||||
user_login(user=user, request=request)
|
user_login(user=user, request=request, is_app=True)
|
||||||
return Response(
|
return Response(
|
||||||
{"message": "Password updated successfully"},
|
{"message": "Password updated successfully"},
|
||||||
status=status.HTTP_200_OK,
|
status=status.HTTP_200_OK,
|
||||||
@ -131,7 +131,7 @@ class SetUserPasswordEndpoint(APIView):
|
|||||||
user.is_password_autoset = False
|
user.is_password_autoset = False
|
||||||
user.save()
|
user.save()
|
||||||
# Login the user as the session is invalidated
|
# Login the user as the session is invalidated
|
||||||
user_login(user=user, request=request)
|
user_login(user=user, request=request, is_app=True)
|
||||||
# Return the user
|
# Return the user
|
||||||
serializer = UserSerializer(user)
|
serializer = UserSerializer(user)
|
||||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||||
|
@ -38,7 +38,7 @@ class SignInAuthSpaceEndpoint(View):
|
|||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request, is_space=True),
|
base_host(request=request, is_space=True),
|
||||||
"accounts/sign-in?" + urlencode(params),
|
"?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ class SignInAuthSpaceEndpoint(View):
|
|||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request, is_space=True),
|
base_host(request=request, is_space=True),
|
||||||
"spaces/accounts/sign-in?" + urlencode(params),
|
"?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@ -79,7 +79,7 @@ class SignInAuthSpaceEndpoint(View):
|
|||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request, is_space=True),
|
base_host(request=request, is_space=True),
|
||||||
"spaces/accounts/sign-in?" + urlencode(params),
|
"?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@ -94,7 +94,7 @@ class SignInAuthSpaceEndpoint(View):
|
|||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request, is_space=True),
|
base_host(request=request, is_space=True),
|
||||||
"spaces/accounts/sign-in?" + urlencode(params),
|
"?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@ -104,11 +104,11 @@ class SignInAuthSpaceEndpoint(View):
|
|||||||
)
|
)
|
||||||
user = provider.authenticate()
|
user = provider.authenticate()
|
||||||
# Login the user and record his device info
|
# Login the user and record his device info
|
||||||
user_login(request=request, user=user)
|
user_login(request=request, user=user, is_space=True)
|
||||||
# redirect to next path
|
# redirect to next path
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request, is_space=True),
|
base_host(request=request, is_space=True),
|
||||||
str(next_path) if next_path else "/",
|
str(next_path) if next_path else "",
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
except AuthenticationException as e:
|
except AuthenticationException as e:
|
||||||
@ -117,7 +117,7 @@ class SignInAuthSpaceEndpoint(View):
|
|||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request, is_space=True),
|
base_host(request=request, is_space=True),
|
||||||
"spaces/accounts/sign-in?" + urlencode(params),
|
"?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@ -141,7 +141,7 @@ class SignUpAuthSpaceEndpoint(View):
|
|||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request, is_space=True),
|
base_host(request=request, is_space=True),
|
||||||
"spaces?" + urlencode(params),
|
"?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@ -162,7 +162,7 @@ class SignUpAuthSpaceEndpoint(View):
|
|||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request, is_space=True),
|
base_host(request=request, is_space=True),
|
||||||
"spaces?" + urlencode(params),
|
"?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
# Validate the email
|
# Validate the email
|
||||||
@ -181,7 +181,7 @@ class SignUpAuthSpaceEndpoint(View):
|
|||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request, is_space=True),
|
base_host(request=request, is_space=True),
|
||||||
"spaces?" + urlencode(params),
|
"?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@ -196,7 +196,7 @@ class SignUpAuthSpaceEndpoint(View):
|
|||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request, is_space=True),
|
base_host(request=request, is_space=True),
|
||||||
"spaces?" + urlencode(params),
|
"?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@ -206,11 +206,11 @@ class SignUpAuthSpaceEndpoint(View):
|
|||||||
)
|
)
|
||||||
user = provider.authenticate()
|
user = provider.authenticate()
|
||||||
# Login the user and record his device info
|
# Login the user and record his device info
|
||||||
user_login(request=request, user=user)
|
user_login(request=request, user=user, is_space=True)
|
||||||
# redirect to referer path
|
# redirect to referer path
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request, is_space=True),
|
base_host(request=request, is_space=True),
|
||||||
str(next_path) if next_path else "spaces",
|
str(next_path) if next_path else "",
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
except AuthenticationException as e:
|
except AuthenticationException as e:
|
||||||
@ -219,6 +219,6 @@ class SignUpAuthSpaceEndpoint(View):
|
|||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request, is_space=True),
|
base_host(request=request, is_space=True),
|
||||||
"spaces?" + urlencode(params),
|
"?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
@ -55,7 +55,7 @@ class GitHubOauthInitiateSpaceEndpoint(View):
|
|||||||
if next_path:
|
if next_path:
|
||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request),
|
base_host(request=request, is_space=True),
|
||||||
"?" + urlencode(params),
|
"?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
@ -108,10 +108,10 @@ class GitHubCallbackSpaceEndpoint(View):
|
|||||||
)
|
)
|
||||||
user = provider.authenticate()
|
user = provider.authenticate()
|
||||||
# Login the user and record his device info
|
# Login the user and record his device info
|
||||||
user_login(request=request, user=user)
|
user_login(request=request, user=user, is_space=True)
|
||||||
# Process workspace and project invitations
|
# Process workspace and project invitations
|
||||||
# redirect to referer path
|
# redirect to referer path
|
||||||
url = urljoin(base_host, str(next_path) if next_path else "/")
|
url = urljoin(base_host, str(next_path) if next_path else "")
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
except AuthenticationException as e:
|
except AuthenticationException as e:
|
||||||
params = e.get_error_dict()
|
params = e.get_error_dict()
|
||||||
|
@ -103,7 +103,7 @@ class GoogleCallbackSpaceEndpoint(View):
|
|||||||
)
|
)
|
||||||
user = provider.authenticate()
|
user = provider.authenticate()
|
||||||
# Login the user and record his device info
|
# Login the user and record his device info
|
||||||
user_login(request=request, user=user)
|
user_login(request=request, user=user, is_space=True)
|
||||||
# redirect to referer path
|
# redirect to referer path
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host, str(next_path) if next_path else "/spaces"
|
base_host, str(next_path) if next_path else "/spaces"
|
||||||
|
@ -86,7 +86,7 @@ class MagicSignInSpaceEndpoint(View):
|
|||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request, is_space=True),
|
base_host(request=request, is_space=True),
|
||||||
"spaces/accounts/sign-in?" + urlencode(params),
|
"?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@ -99,7 +99,7 @@ class MagicSignInSpaceEndpoint(View):
|
|||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request, is_space=True),
|
base_host(request=request, is_space=True),
|
||||||
"accounts/sign-in?" + urlencode(params),
|
"?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@ -109,14 +109,14 @@ class MagicSignInSpaceEndpoint(View):
|
|||||||
)
|
)
|
||||||
user = provider.authenticate()
|
user = provider.authenticate()
|
||||||
# Login the user and record his device info
|
# Login the user and record his device info
|
||||||
user_login(request=request, user=user)
|
user_login(request=request, user=user, is_space=True)
|
||||||
# redirect to referer path
|
# redirect to referer path
|
||||||
profile = Profile.objects.get(user=user)
|
profile = Profile.objects.get(user=user)
|
||||||
if user.is_password_autoset and profile.is_onboarded:
|
if user.is_password_autoset and profile.is_onboarded:
|
||||||
path = "spaces/accounts/set-password"
|
path = "accounts/set-password"
|
||||||
else:
|
else:
|
||||||
# Get the redirection path
|
# Get the redirection path
|
||||||
path = str(next_path) if next_path else "spaces"
|
path = str(next_path) if next_path else ""
|
||||||
url = urljoin(base_host(request=request, is_space=True), path)
|
url = urljoin(base_host(request=request, is_space=True), path)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@ -126,7 +126,7 @@ class MagicSignInSpaceEndpoint(View):
|
|||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request, is_space=True),
|
base_host(request=request, is_space=True),
|
||||||
"spaces/accounts/sign-in?" + urlencode(params),
|
"?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@ -152,7 +152,7 @@ class MagicSignUpSpaceEndpoint(View):
|
|||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request, is_space=True),
|
base_host(request=request, is_space=True),
|
||||||
"spaces/accounts/sign-in?" + urlencode(params),
|
"?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@ -176,7 +176,7 @@ class MagicSignUpSpaceEndpoint(View):
|
|||||||
)
|
)
|
||||||
user = provider.authenticate()
|
user = provider.authenticate()
|
||||||
# Login the user and record his device info
|
# Login the user and record his device info
|
||||||
user_login(request=request, user=user)
|
user_login(request=request, user=user, is_space=True)
|
||||||
# redirect to referer path
|
# redirect to referer path
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request, is_space=True),
|
base_host(request=request, is_space=True),
|
||||||
@ -190,6 +190,6 @@ class MagicSignUpSpaceEndpoint(View):
|
|||||||
params["next_path"] = str(next_path)
|
params["next_path"] = str(next_path)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request, is_space=True),
|
base_host(request=request, is_space=True),
|
||||||
"spaces/accounts/sign-in?" + urlencode(params),
|
"?" + urlencode(params),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
@ -183,11 +183,9 @@ class ResetPasswordSpaceEndpoint(View):
|
|||||||
user.is_password_autoset = False
|
user.is_password_autoset = False
|
||||||
user.save()
|
user.save()
|
||||||
|
|
||||||
url = urljoin(
|
return HttpResponseRedirect(
|
||||||
base_host(request=request, is_space=True),
|
base_host(request=request, is_space=True)
|
||||||
"accounts/sign-in?" + urlencode({"success": True}),
|
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
|
||||||
except DjangoUnicodeDecodeError:
|
except DjangoUnicodeDecodeError:
|
||||||
exc = AuthenticationException(
|
exc = AuthenticationException(
|
||||||
error_code=AUTHENTICATION_ERROR_CODES[
|
error_code=AUTHENTICATION_ERROR_CODES[
|
||||||
|
@ -23,12 +23,10 @@ class SignOutAuthSpaceEndpoint(View):
|
|||||||
user.save()
|
user.save()
|
||||||
# Log the user out
|
# Log the user out
|
||||||
logout(request)
|
logout(request)
|
||||||
url = urljoin(
|
return HttpResponseRedirect(
|
||||||
base_host(request=request, is_space=True),
|
base_host(request=request, is_space=True)
|
||||||
"accounts/sign-in?" + urlencode({"success": "true"}),
|
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
|
||||||
except Exception:
|
except Exception:
|
||||||
return HttpResponseRedirect(
|
return HttpResponseRedirect(
|
||||||
base_host(request=request, is_space=True), "accounts/sign-in"
|
base_host(request=request, is_space=True)
|
||||||
)
|
)
|
||||||
|
@ -107,7 +107,7 @@ class InstanceAdminSignUpEndpoint(View):
|
|||||||
)
|
)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request, is_admin=True),
|
base_host(request=request, is_admin=True),
|
||||||
"god-mode/setup?" + urlencode(exc.get_error_dict()),
|
"setup?" + urlencode(exc.get_error_dict()),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@ -119,7 +119,7 @@ class InstanceAdminSignUpEndpoint(View):
|
|||||||
)
|
)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request, is_admin=True),
|
base_host(request=request, is_admin=True),
|
||||||
"god-mode/setup?" + urlencode(exc.get_error_dict()),
|
"setup?" + urlencode(exc.get_error_dict()),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@ -148,7 +148,7 @@ class InstanceAdminSignUpEndpoint(View):
|
|||||||
)
|
)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request, is_admin=True),
|
base_host(request=request, is_admin=True),
|
||||||
"god-mode/setup?" + urlencode(exc.get_error_dict()),
|
"setup?" + urlencode(exc.get_error_dict()),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@ -170,7 +170,7 @@ class InstanceAdminSignUpEndpoint(View):
|
|||||||
)
|
)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request, is_admin=True),
|
base_host(request=request, is_admin=True),
|
||||||
"god-mode/setup?" + urlencode(exc.get_error_dict()),
|
"setup?" + urlencode(exc.get_error_dict()),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@ -192,7 +192,7 @@ class InstanceAdminSignUpEndpoint(View):
|
|||||||
)
|
)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request, is_admin=True),
|
base_host(request=request, is_admin=True),
|
||||||
"god-mode/setup?" + urlencode(exc.get_error_dict()),
|
"setup?" + urlencode(exc.get_error_dict()),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
else:
|
else:
|
||||||
@ -214,7 +214,7 @@ class InstanceAdminSignUpEndpoint(View):
|
|||||||
)
|
)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request, is_admin=True),
|
base_host(request=request, is_admin=True),
|
||||||
"god-mode/setup?" + urlencode(exc.get_error_dict()),
|
"setup?" + urlencode(exc.get_error_dict()),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@ -247,10 +247,8 @@ class InstanceAdminSignUpEndpoint(View):
|
|||||||
instance.save()
|
instance.save()
|
||||||
|
|
||||||
# get tokens for user
|
# get tokens for user
|
||||||
user_login(request=request, user=user)
|
user_login(request=request, user=user, is_admin=True)
|
||||||
url = urljoin(
|
url = urljoin(base_host(request=request, is_admin=True), "general")
|
||||||
base_host(request=request, is_admin=True), "god-mode/general"
|
|
||||||
)
|
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
|
|
||||||
@ -272,7 +270,7 @@ class InstanceAdminSignInEndpoint(View):
|
|||||||
)
|
)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request, is_admin=True),
|
base_host(request=request, is_admin=True),
|
||||||
"god-mode/login?" + urlencode(exc.get_error_dict()),
|
"?" + urlencode(exc.get_error_dict()),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@ -293,7 +291,7 @@ class InstanceAdminSignInEndpoint(View):
|
|||||||
)
|
)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request, is_admin=True),
|
base_host(request=request, is_admin=True),
|
||||||
"god-mode/login?" + urlencode(exc.get_error_dict()),
|
"?" + urlencode(exc.get_error_dict()),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@ -311,7 +309,7 @@ class InstanceAdminSignInEndpoint(View):
|
|||||||
)
|
)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request, is_admin=True),
|
base_host(request=request, is_admin=True),
|
||||||
"god-mode/login?" + urlencode(exc.get_error_dict()),
|
"?" + urlencode(exc.get_error_dict()),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@ -331,7 +329,7 @@ class InstanceAdminSignInEndpoint(View):
|
|||||||
)
|
)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request, is_admin=True),
|
base_host(request=request, is_admin=True),
|
||||||
"god-mode/login?" + urlencode(exc.get_error_dict()),
|
"?" + urlencode(exc.get_error_dict()),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@ -348,7 +346,7 @@ class InstanceAdminSignInEndpoint(View):
|
|||||||
)
|
)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request, is_admin=True),
|
base_host(request=request, is_admin=True),
|
||||||
"god-mode/login?" + urlencode(exc.get_error_dict()),
|
"?" + urlencode(exc.get_error_dict()),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@ -365,7 +363,7 @@ class InstanceAdminSignInEndpoint(View):
|
|||||||
)
|
)
|
||||||
url = urljoin(
|
url = urljoin(
|
||||||
base_host(request=request, is_admin=True),
|
base_host(request=request, is_admin=True),
|
||||||
"god-mode/login?" + urlencode(exc.get_error_dict()),
|
"?" + urlencode(exc.get_error_dict()),
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
# settings last active for the user
|
# settings last active for the user
|
||||||
@ -378,10 +376,8 @@ class InstanceAdminSignInEndpoint(View):
|
|||||||
user.save()
|
user.save()
|
||||||
|
|
||||||
# get tokens for user
|
# get tokens for user
|
||||||
user_login(request=request, user=user)
|
user_login(request=request, user=user, is_admin=True)
|
||||||
url = urljoin(
|
url = urljoin(base_host(request=request, is_admin=True), "general")
|
||||||
base_host(request=request, is_admin=True), "god-mode/general"
|
|
||||||
)
|
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
|
|
||||||
@ -414,12 +410,9 @@ class InstanceAdminSignOutEndpoint(View):
|
|||||||
user.save()
|
user.save()
|
||||||
# Log the user out
|
# Log the user out
|
||||||
logout(request)
|
logout(request)
|
||||||
url = urljoin(
|
url = urljoin(base_host(request=request, is_admin=True))
|
||||||
base_host(request=request, is_admin=True),
|
|
||||||
"accounts/sign-in?" + urlencode({"success": "true"}),
|
|
||||||
)
|
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
except Exception:
|
except Exception:
|
||||||
return HttpResponseRedirect(
|
return HttpResponseRedirect(
|
||||||
base_host(request=request, is_admin=True), "accounts/sign-in"
|
base_host(request=request, is_admin=True)
|
||||||
)
|
)
|
||||||
|
@ -346,4 +346,4 @@ CSRF_COOKIE_DOMAIN = os.environ.get("COOKIE_DOMAIN", None)
|
|||||||
# Base URLs
|
# Base URLs
|
||||||
ADMIN_BASE_URL = os.environ.get("ADMIN_BASE_URL", None)
|
ADMIN_BASE_URL = os.environ.get("ADMIN_BASE_URL", None)
|
||||||
SPACE_BASE_URL = os.environ.get("SPACE_BASE_URL", None)
|
SPACE_BASE_URL = os.environ.get("SPACE_BASE_URL", None)
|
||||||
APP_BASE_URL = os.environ.get("ADMIN_BASE_URL", None)
|
APP_BASE_URL = os.environ.get("APP_BASE_URL") or os.environ.get("WEB_URL")
|
||||||
|
@ -35,10 +35,10 @@ CORS_ALLOWED_ORIGINS = [
|
|||||||
"http://127.0.0.1",
|
"http://127.0.0.1",
|
||||||
"http://localhost:3000",
|
"http://localhost:3000",
|
||||||
"http://127.0.0.1:3000",
|
"http://127.0.0.1:3000",
|
||||||
"http://localhost:4000",
|
"http://localhost:3001",
|
||||||
"http://127.0.0.1:4000",
|
"http://127.0.0.1:3001",
|
||||||
"http://localhost:3333",
|
"http://localhost:3002",
|
||||||
"http://127.0.0.1:3333",
|
"http://127.0.0.1:3002",
|
||||||
]
|
]
|
||||||
CSRF_TRUSTED_ORIGINS = CORS_ALLOWED_ORIGINS
|
CSRF_TRUSTED_ORIGINS = CORS_ALLOWED_ORIGINS
|
||||||
CORS_ALLOW_ALL_ORIGINS = True
|
CORS_ALLOW_ALL_ORIGINS = True
|
||||||
|
@ -12,8 +12,6 @@ SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
|
|||||||
|
|
||||||
INSTALLED_APPS += ("scout_apm.django",) # noqa
|
INSTALLED_APPS += ("scout_apm.django",) # noqa
|
||||||
|
|
||||||
# Honor the 'X-Forwarded-Proto' header for request.is_secure()
|
|
||||||
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
|
|
||||||
|
|
||||||
# Scout Settings
|
# Scout Settings
|
||||||
SCOUT_MONITOR = os.environ.get("SCOUT_MONITOR", False)
|
SCOUT_MONITOR = os.environ.get("SCOUT_MONITOR", False)
|
||||||
|
@ -1 +1 @@
|
|||||||
python-3.11.9
|
python-3.12.3
|
@ -140,8 +140,11 @@ export const OnBoardingForm: React.FC<Props> = observer((props) => {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-4">
|
<div className="flex gap-4">
|
||||||
<div className="space-y-1">
|
<div className="space-y-1 w-full">
|
||||||
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="first_name">
|
<label
|
||||||
|
className="text-sm text-onboarding-text-300 font-medium after:content-['*'] after:ml-0.5 after:text-red-500"
|
||||||
|
htmlFor="first_name"
|
||||||
|
>
|
||||||
First name
|
First name
|
||||||
</label>
|
</label>
|
||||||
<Controller
|
<Controller
|
||||||
@ -171,8 +174,11 @@ export const OnBoardingForm: React.FC<Props> = observer((props) => {
|
|||||||
/>
|
/>
|
||||||
{errors.first_name && <span className="text-sm text-red-500">{errors.first_name.message}</span>}
|
{errors.first_name && <span className="text-sm text-red-500">{errors.first_name.message}</span>}
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1 w-full">
|
||||||
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="last_name">
|
<label
|
||||||
|
className="text-sm text-onboarding-text-300 font-medium after:content-['*'] after:ml-0.5 after:text-red-500"
|
||||||
|
htmlFor="last_name"
|
||||||
|
>
|
||||||
Last name
|
Last name
|
||||||
</label>
|
</label>
|
||||||
<Controller
|
<Controller
|
||||||
|
@ -6,5 +6,5 @@ import { IProfileStore } from "@/store/user/profile.store";
|
|||||||
export const useUserProfile = (): IProfileStore => {
|
export const useUserProfile = (): IProfileStore => {
|
||||||
const context = useContext(StoreContext);
|
const context = useContext(StoreContext);
|
||||||
if (context === undefined) throw new Error("useUserProfile must be used within StoreProvider");
|
if (context === undefined) throw new Error("useUserProfile must be used within StoreProvider");
|
||||||
return context.profile;
|
return context.user.userProfile;
|
||||||
};
|
};
|
||||||
|
@ -32,13 +32,17 @@ export const AuthWrapper: FC<TAuthWrapper> = observer((props) => {
|
|||||||
<Spinner />
|
<Spinner />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (pageType === EPageTypes.PUBLIC) return <>{children}</>;
|
if (pageType === EPageTypes.PUBLIC) return <>{children}</>;
|
||||||
|
|
||||||
if (pageType === EPageTypes.INIT) {
|
if (pageType === EPageTypes.INIT) {
|
||||||
if (!currentUser?.id) return <>{children}</>;
|
if (!currentUser?.id) return <>{children}</>;
|
||||||
else {
|
else {
|
||||||
if (currentUserProfile?.id && currentUserProfile?.onboarding_step?.profile_complete) return <>{children}</>;
|
if (
|
||||||
|
currentUserProfile &&
|
||||||
|
currentUserProfile?.id &&
|
||||||
|
Boolean(currentUserProfile?.onboarding_step?.profile_complete)
|
||||||
|
)
|
||||||
|
return <>{children}</>;
|
||||||
else {
|
else {
|
||||||
router.push(`/onboarding`);
|
router.push(`/onboarding`);
|
||||||
return <></>;
|
return <></>;
|
||||||
|
@ -16,7 +16,7 @@ import { useUser, useUserProfile } from "@/hooks/store";
|
|||||||
import { AuthWrapper } from "@/lib/wrappers";
|
import { AuthWrapper } from "@/lib/wrappers";
|
||||||
// assets
|
// assets
|
||||||
import ProfileSetupDark from "public/onboarding/profile-setup-dark.svg";
|
import ProfileSetupDark from "public/onboarding/profile-setup-dark.svg";
|
||||||
import ProfileSetup from "public/onboarding/profile-setup.svg";
|
import ProfileSetup from "public/onboarding/profile-setup-light.svg";
|
||||||
|
|
||||||
const OnBoardingPage = observer(() => {
|
const OnBoardingPage = observer(() => {
|
||||||
// router
|
// router
|
||||||
@ -47,8 +47,8 @@ const OnBoardingPage = observer(() => {
|
|||||||
console.log("Failed to update onboarding status");
|
console.log("Failed to update onboarding status");
|
||||||
});
|
});
|
||||||
|
|
||||||
if (next_path) router.replace(next_path.toString());
|
if (next_path) router.push(next_path.toString());
|
||||||
router.replace("/");
|
router.push("/");
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 729 KiB After Width: | Height: | Size: 994 KiB |
407
space/public/onboarding/profile-setup-light.svg
Normal file
407
space/public/onboarding/profile-setup-light.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 993 KiB |
@ -4,7 +4,6 @@ import { enableStaticRendering } from "mobx-react-lite";
|
|||||||
import { IInstanceStore, InstanceStore } from "@/store/instance.store";
|
import { IInstanceStore, InstanceStore } from "@/store/instance.store";
|
||||||
import { IProjectStore, ProjectStore } from "@/store/project";
|
import { IProjectStore, ProjectStore } from "@/store/project";
|
||||||
import { IUserStore, UserStore } from "@/store/user";
|
import { IUserStore, UserStore } from "@/store/user";
|
||||||
import { IProfileStore, ProfileStore } from "@/store/user/profile.store";
|
|
||||||
|
|
||||||
import IssueStore, { IIssueStore } from "./issue";
|
import IssueStore, { IIssueStore } from "./issue";
|
||||||
import IssueDetailStore, { IIssueDetailStore } from "./issue_details";
|
import IssueDetailStore, { IIssueDetailStore } from "./issue_details";
|
||||||
@ -16,7 +15,6 @@ enableStaticRendering(typeof window === "undefined");
|
|||||||
export class RootStore {
|
export class RootStore {
|
||||||
instance: IInstanceStore;
|
instance: IInstanceStore;
|
||||||
user: IUserStore;
|
user: IUserStore;
|
||||||
profile: IProfileStore;
|
|
||||||
project: IProjectStore;
|
project: IProjectStore;
|
||||||
|
|
||||||
issue: IIssueStore;
|
issue: IIssueStore;
|
||||||
@ -27,9 +25,8 @@ export class RootStore {
|
|||||||
constructor() {
|
constructor() {
|
||||||
this.instance = new InstanceStore(this);
|
this.instance = new InstanceStore(this);
|
||||||
this.user = new UserStore(this);
|
this.user = new UserStore(this);
|
||||||
this.profile = new ProfileStore(this);
|
|
||||||
this.project = new ProjectStore(this);
|
|
||||||
|
|
||||||
|
this.project = new ProjectStore(this);
|
||||||
this.issue = new IssueStore(this);
|
this.issue = new IssueStore(this);
|
||||||
this.issueDetails = new IssueDetailStore(this);
|
this.issueDetails = new IssueDetailStore(this);
|
||||||
this.mentionsStore = new MentionsStore(this);
|
this.mentionsStore = new MentionsStore(this);
|
||||||
@ -41,7 +38,6 @@ export class RootStore {
|
|||||||
|
|
||||||
this.instance = new InstanceStore(this);
|
this.instance = new InstanceStore(this);
|
||||||
this.user = new UserStore(this);
|
this.user = new UserStore(this);
|
||||||
this.profile = new ProfileStore(this);
|
|
||||||
this.project = new ProjectStore(this);
|
this.project = new ProjectStore(this);
|
||||||
|
|
||||||
this.issue = new IssueStore(this);
|
this.issue = new IssueStore(this);
|
||||||
|
@ -74,7 +74,7 @@ ENV NEXT_PUBLIC_ADMIN_BASE_URL=$NEXT_PUBLIC_ADMIN_BASE_URL
|
|||||||
ARG NEXT_PUBLIC_ADMIN_BASE_PATH="/god-mode"
|
ARG NEXT_PUBLIC_ADMIN_BASE_PATH="/god-mode"
|
||||||
ENV NEXT_PUBLIC_ADMIN_BASE_PATH=$NEXT_PUBLIC_ADMIN_BASE_PATH
|
ENV NEXT_PUBLIC_ADMIN_BASE_PATH=$NEXT_PUBLIC_ADMIN_BASE_PATH
|
||||||
|
|
||||||
ARG NEXT_PUBLIC_SPACE_BASE_URL=""
|
ARG NEXT_PUBLIC_SPACE_BASE_URL="/spaces"
|
||||||
ENV NEXT_PUBLIC_SPACE_BASE_URL=$NEXT_PUBLIC_SPACE_BASE_URL
|
ENV NEXT_PUBLIC_SPACE_BASE_URL=$NEXT_PUBLIC_SPACE_BASE_URL
|
||||||
|
|
||||||
ARG NEXT_PUBLIC_SPACE_BASE_PATH="/spaces"
|
ARG NEXT_PUBLIC_SPACE_BASE_PATH="/spaces"
|
||||||
|
@ -2,7 +2,6 @@ import React, { FC, useEffect, useState } from "react";
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { IEmailCheckData } from "@plane/types";
|
import { IEmailCheckData } from "@plane/types";
|
||||||
import { TOAST_TYPE, setToast } from "@plane/ui";
|
|
||||||
// components
|
// components
|
||||||
import {
|
import {
|
||||||
AuthHeader,
|
AuthHeader,
|
||||||
@ -43,6 +42,7 @@ export const AuthRoot: FC<TAuthRoot> = observer((props) => {
|
|||||||
const [authStep, setAuthStep] = useState<EAuthSteps>(EAuthSteps.EMAIL);
|
const [authStep, setAuthStep] = useState<EAuthSteps>(EAuthSteps.EMAIL);
|
||||||
const [email, setEmail] = useState(emailParam ? emailParam.toString() : "");
|
const [email, setEmail] = useState(emailParam ? emailParam.toString() : "");
|
||||||
const [errorInfo, setErrorInfo] = useState<TAuthErrorInfo | undefined>(undefined);
|
const [errorInfo, setErrorInfo] = useState<TAuthErrorInfo | undefined>(undefined);
|
||||||
|
const [isPasswordAutoset, setIsPasswordAutoset] = useState(true);
|
||||||
// hooks
|
// hooks
|
||||||
const { instance } = useInstance();
|
const { instance } = useInstance();
|
||||||
|
|
||||||
@ -63,52 +63,57 @@ export const AuthRoot: FC<TAuthRoot> = observer((props) => {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
setAuthStep(EAuthSteps.UNIQUE_CODE);
|
setAuthStep(EAuthSteps.UNIQUE_CODE);
|
||||||
|
setErrorInfo(errorhandler);
|
||||||
// validating weather to show alert to banner
|
|
||||||
if (errorhandler?.type === EErrorAlertType.TOAST_ALERT) {
|
|
||||||
setToast({
|
|
||||||
type: TOAST_TYPE.ERROR,
|
|
||||||
title: errorhandler?.title,
|
|
||||||
message: errorhandler?.message as string,
|
|
||||||
});
|
|
||||||
} else setErrorInfo(errorhandler);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [error_code, authMode]);
|
}, [error_code, authMode]);
|
||||||
|
|
||||||
// step 1 submit handler- email verification
|
// submit handler- email verification
|
||||||
const handleEmailVerification = async (data: IEmailCheckData) => {
|
const handleEmailVerification = async (data: IEmailCheckData) => {
|
||||||
setEmail(data.email);
|
setEmail(data.email);
|
||||||
|
|
||||||
const emailCheckRequest =
|
const emailCheckRequest =
|
||||||
authMode === EAuthModes.SIGN_IN ? authService.signInEmailCheck(data) : authService.signUpEmailCheck(data);
|
authMode === EAuthModes.SIGN_IN ? authService.signInEmailCheck(data) : authService.signUpEmailCheck(data);
|
||||||
|
|
||||||
await emailCheckRequest
|
await emailCheckRequest
|
||||||
.then((response) => {
|
.then(async (response) => {
|
||||||
if (authMode === EAuthModes.SIGN_IN) {
|
if (authMode === EAuthModes.SIGN_IN) {
|
||||||
if (response.is_password_autoset) setAuthStep(EAuthSteps.UNIQUE_CODE);
|
if (response.is_password_autoset) {
|
||||||
else setAuthStep(EAuthSteps.PASSWORD);
|
setAuthStep(EAuthSteps.UNIQUE_CODE);
|
||||||
|
generateEmailUniqueCode(data.email);
|
||||||
|
} else {
|
||||||
|
setIsPasswordAutoset(false);
|
||||||
|
setAuthStep(EAuthSteps.PASSWORD);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (instance && instance?.config?.is_smtp_configured) setAuthStep(EAuthSteps.UNIQUE_CODE);
|
if (instance && instance?.config?.is_smtp_configured) {
|
||||||
else setAuthStep(EAuthSteps.PASSWORD);
|
setAuthStep(EAuthSteps.UNIQUE_CODE);
|
||||||
|
generateEmailUniqueCode(data.email);
|
||||||
|
} else setAuthStep(EAuthSteps.PASSWORD);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
const errorhandler = authErrorHandler(error?.error_code.toString(), data?.email || undefined);
|
const errorhandler = authErrorHandler(error?.error_code.toString(), data?.email || undefined);
|
||||||
if (errorhandler?.type === EErrorAlertType.BANNER_ALERT) {
|
if (errorhandler?.type) setErrorInfo(errorhandler);
|
||||||
setErrorInfo(errorhandler);
|
});
|
||||||
return;
|
};
|
||||||
} else if (errorhandler?.type === EErrorAlertType.TOAST_ALERT)
|
|
||||||
setToast({
|
// generating the unique code
|
||||||
type: TOAST_TYPE.ERROR,
|
const generateEmailUniqueCode = async (email: string): Promise<{ code: string } | undefined> => {
|
||||||
title: errorhandler?.title,
|
const payload = { email: email };
|
||||||
message: (errorhandler?.message as string) || "Something went wrong. Please try again.",
|
return await authService
|
||||||
});
|
.generateUniqueCode(payload)
|
||||||
|
.then(() => ({ code: "" }))
|
||||||
|
.catch((error) => {
|
||||||
|
const errorhandler = authErrorHandler(error?.error_code.toString());
|
||||||
|
if (errorhandler?.type) setErrorInfo(errorhandler);
|
||||||
|
throw error;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const isOAuthEnabled =
|
const isOAuthEnabled =
|
||||||
instance?.config && (instance?.config?.is_google_enabled || instance?.config?.is_github_enabled);
|
(instance?.config && (instance?.config?.is_google_enabled || instance?.config?.is_github_enabled)) || false;
|
||||||
|
|
||||||
|
const isSMTPConfigured = (instance?.config && instance?.config?.is_smtp_configured) || false;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex flex-col space-y-6">
|
<div className="relative flex flex-col space-y-6">
|
||||||
@ -125,24 +130,29 @@ export const AuthRoot: FC<TAuthRoot> = observer((props) => {
|
|||||||
{authStep === EAuthSteps.EMAIL && <AuthEmailForm defaultEmail={email} onSubmit={handleEmailVerification} />}
|
{authStep === EAuthSteps.EMAIL && <AuthEmailForm defaultEmail={email} onSubmit={handleEmailVerification} />}
|
||||||
{authStep === EAuthSteps.UNIQUE_CODE && (
|
{authStep === EAuthSteps.UNIQUE_CODE && (
|
||||||
<AuthUniqueCodeForm
|
<AuthUniqueCodeForm
|
||||||
|
mode={authMode}
|
||||||
email={email}
|
email={email}
|
||||||
handleEmailClear={() => {
|
handleEmailClear={() => {
|
||||||
setEmail("");
|
setEmail("");
|
||||||
setAuthStep(EAuthSteps.EMAIL);
|
setAuthStep(EAuthSteps.EMAIL);
|
||||||
}}
|
}}
|
||||||
submitButtonText="Continue"
|
generateEmailUniqueCode={generateEmailUniqueCode}
|
||||||
mode={authMode}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{authStep === EAuthSteps.PASSWORD && (
|
{authStep === EAuthSteps.PASSWORD && (
|
||||||
<AuthPasswordForm
|
<AuthPasswordForm
|
||||||
|
mode={authMode}
|
||||||
|
isPasswordAutoset={isPasswordAutoset}
|
||||||
|
isSMTPConfigured={isSMTPConfigured}
|
||||||
email={email}
|
email={email}
|
||||||
handleEmailClear={() => {
|
handleEmailClear={() => {
|
||||||
setEmail("");
|
setEmail("");
|
||||||
setAuthStep(EAuthSteps.EMAIL);
|
setAuthStep(EAuthSteps.EMAIL);
|
||||||
}}
|
}}
|
||||||
handleStepChange={(step) => setAuthStep(step)}
|
handleAuthStep={(step: EAuthSteps) => {
|
||||||
mode={authMode}
|
if (step === EAuthSteps.UNIQUE_CODE) generateEmailUniqueCode(email);
|
||||||
|
setAuthStep(step);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{isOAuthEnabled && <OAuthOptions />}
|
{isOAuthEnabled && <OAuthOptions />}
|
||||||
|
@ -7,6 +7,7 @@ import { IEmailCheckData } from "@plane/types";
|
|||||||
// ui
|
// ui
|
||||||
import { Button, Input, Spinner } from "@plane/ui";
|
import { Button, Input, Spinner } from "@plane/ui";
|
||||||
// helpers
|
// helpers
|
||||||
|
import { cn } from "@/helpers/common.helper";
|
||||||
import { checkEmailValidity } from "@/helpers/string.helper";
|
import { checkEmailValidity } from "@/helpers/string.helper";
|
||||||
|
|
||||||
type TAuthEmailForm = {
|
type TAuthEmailForm = {
|
||||||
@ -19,6 +20,7 @@ export const AuthEmailForm: FC<TAuthEmailForm> = observer((props) => {
|
|||||||
// states
|
// states
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
const [email, setEmail] = useState(defaultEmail);
|
const [email, setEmail] = useState(defaultEmail);
|
||||||
|
const [isFocused, setFocused] = useState(false);
|
||||||
|
|
||||||
const emailError = useMemo(
|
const emailError = useMemo(
|
||||||
() => (email && !checkEmailValidity(email) ? { email: "Email is invalid" } : undefined),
|
() => (email && !checkEmailValidity(email) ? { email: "Email is invalid" } : undefined),
|
||||||
@ -38,31 +40,36 @@ export const AuthEmailForm: FC<TAuthEmailForm> = observer((props) => {
|
|||||||
const isButtonDisabled = email.length === 0 || Boolean(emailError?.email) || isSubmitting;
|
const isButtonDisabled = email.length === 0 || Boolean(emailError?.email) || isSubmitting;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleFormSubmit} className="mt-8 space-y-4">
|
<form onSubmit={handleFormSubmit} className="mt-5 space-y-4">
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="email">
|
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="email">
|
||||||
Email
|
Email
|
||||||
</label>
|
</label>
|
||||||
<div className="relative flex items-center rounded-md bg-onboarding-background-200">
|
<div
|
||||||
|
className={cn(
|
||||||
|
`relative flex items-center rounded-md bg-onboarding-background-200 border`,
|
||||||
|
!isFocused && Boolean(emailError?.email) ? `border-red-500` : `border-onboarding-border-100`
|
||||||
|
)}
|
||||||
|
>
|
||||||
<Input
|
<Input
|
||||||
id="email"
|
id="email"
|
||||||
name="email"
|
name="email"
|
||||||
type="email"
|
type="email"
|
||||||
value={email}
|
value={email}
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
hasError={Boolean(emailError?.email)}
|
|
||||||
placeholder="name@company.com"
|
placeholder="name@company.com"
|
||||||
className="h-[46px] w-full border border-onboarding-border-100 pr-12 placeholder:text-onboarding-text-400"
|
className={`disable-autofill-style h-[46px] w-full placeholder:text-onboarding-text-400 autofill:bg-red-500 border-0 focus:bg-none active:bg-transparent`}
|
||||||
|
onFocus={() => setFocused(true)}
|
||||||
|
onBlur={() => setFocused(false)}
|
||||||
autoFocus
|
autoFocus
|
||||||
/>
|
/>
|
||||||
{email.length > 0 && (
|
{email.length > 0 && (
|
||||||
<XCircle
|
<div className="flex-shrink-0 h-5 w-5 mr-2 bg-onboarding-background-200 hover:cursor-pointer">
|
||||||
className="absolute right-3 h-5 w-5 stroke-custom-text-400 hover:cursor-pointer"
|
<XCircle className="h-5 w-5 stroke-custom-text-400" onClick={() => setEmail("")} />
|
||||||
onClick={() => setEmail("")}
|
</div>
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{emailError?.email && (
|
{emailError?.email && !isFocused && (
|
||||||
<p className="flex items-center gap-1 text-xs text-red-600 px-0.5">
|
<p className="flex items-center gap-1 text-xs text-red-600 px-0.5">
|
||||||
<CircleAlert height={12} width={12} />
|
<CircleAlert height={12} width={12} />
|
||||||
{emailError.email}
|
{emailError.email}
|
||||||
|
@ -14,15 +14,17 @@ import { EAuthModes, EAuthSteps } from "@/helpers/authentication.helper";
|
|||||||
import { API_BASE_URL } from "@/helpers/common.helper";
|
import { API_BASE_URL } from "@/helpers/common.helper";
|
||||||
import { getPasswordStrength } from "@/helpers/password.helper";
|
import { getPasswordStrength } from "@/helpers/password.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useEventTracker, useInstance } from "@/hooks/store";
|
import { useEventTracker } from "@/hooks/store";
|
||||||
// services
|
// services
|
||||||
import { AuthService } from "@/services/auth.service";
|
import { AuthService } from "@/services/auth.service";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
email: string;
|
email: string;
|
||||||
|
isPasswordAutoset: boolean;
|
||||||
|
isSMTPConfigured: boolean;
|
||||||
mode: EAuthModes;
|
mode: EAuthModes;
|
||||||
handleStepChange: (step: EAuthSteps) => void;
|
|
||||||
handleEmailClear: () => void;
|
handleEmailClear: () => void;
|
||||||
|
handleAuthStep: (step: EAuthSteps) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type TPasswordFormValues = {
|
type TPasswordFormValues = {
|
||||||
@ -39,9 +41,8 @@ const defaultValues: TPasswordFormValues = {
|
|||||||
const authService = new AuthService();
|
const authService = new AuthService();
|
||||||
|
|
||||||
export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
|
export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
|
||||||
const { email, handleStepChange, handleEmailClear, mode } = props;
|
const { email, isSMTPConfigured, handleAuthStep, handleEmailClear, mode } = props;
|
||||||
// hooks
|
// hooks
|
||||||
const { instance } = useInstance();
|
|
||||||
const { captureEvent } = useEventTracker();
|
const { captureEvent } = useEventTracker();
|
||||||
// states
|
// states
|
||||||
const [csrfToken, setCsrfToken] = useState<string | undefined>(undefined);
|
const [csrfToken, setCsrfToken] = useState<string | undefined>(undefined);
|
||||||
@ -56,9 +57,6 @@ export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
|
|||||||
const handleShowPassword = (key: keyof typeof showPassword) =>
|
const handleShowPassword = (key: keyof typeof showPassword) =>
|
||||||
setShowPassword((prev) => ({ ...prev, [key]: !prev[key] }));
|
setShowPassword((prev) => ({ ...prev, [key]: !prev[key] }));
|
||||||
|
|
||||||
// derived values
|
|
||||||
const isSmtpConfigured = instance?.config?.is_smtp_configured;
|
|
||||||
|
|
||||||
const handleFormChange = (key: keyof TPasswordFormValues, value: string) =>
|
const handleFormChange = (key: keyof TPasswordFormValues, value: string) =>
|
||||||
setPasswordFormData((prev) => ({ ...prev, [key]: value }));
|
setPasswordFormData((prev) => ({ ...prev, [key]: value }));
|
||||||
|
|
||||||
@ -68,13 +66,13 @@ export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
|
|||||||
}, [csrfToken]);
|
}, [csrfToken]);
|
||||||
|
|
||||||
const redirectToUniqueCodeSignIn = async () => {
|
const redirectToUniqueCodeSignIn = async () => {
|
||||||
handleStepChange(EAuthSteps.UNIQUE_CODE);
|
handleAuthStep(EAuthSteps.UNIQUE_CODE);
|
||||||
};
|
};
|
||||||
|
|
||||||
const passwordSupport =
|
const passwordSupport =
|
||||||
mode === EAuthModes.SIGN_IN ? (
|
mode === EAuthModes.SIGN_IN ? (
|
||||||
<div className="mt-2 w-full pb-3">
|
<div className="w-full">
|
||||||
{isSmtpConfigured ? (
|
{isSMTPConfigured ? (
|
||||||
<Link
|
<Link
|
||||||
onClick={() => captureEvent(FORGOT_PASSWORD)}
|
onClick={() => captureEvent(FORGOT_PASSWORD)}
|
||||||
href={`/accounts/forgot-password?email=${email}`}
|
href={`/accounts/forgot-password?email=${email}`}
|
||||||
@ -87,7 +85,10 @@ export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
isPasswordInputFocused && <PasswordStrengthMeter password={passwordFormData.password} />
|
passwordFormData.password.length > 0 &&
|
||||||
|
(getPasswordStrength(passwordFormData.password) < 3 || isPasswordInputFocused) && (
|
||||||
|
<PasswordStrengthMeter password={passwordFormData.password} />
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const isButtonDisabled = useMemo(
|
const isButtonDisabled = useMemo(
|
||||||
@ -112,11 +113,14 @@ export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
|
|||||||
onError={() => setIsSubmitting(false)}
|
onError={() => setIsSubmitting(false)}
|
||||||
>
|
>
|
||||||
<input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} />
|
<input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} />
|
||||||
|
<input type="hidden" value={passwordFormData.email} name="email" />
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="email">
|
<label className="text-sm font-medium text-onboarding-text-300" htmlFor="email">
|
||||||
Email
|
Email
|
||||||
</label>
|
</label>
|
||||||
<div className="relative flex items-center rounded-md bg-onboarding-background-200">
|
<div
|
||||||
|
className={`relative flex items-center rounded-md bg-onboarding-background-200 border border-onboarding-border-100`}
|
||||||
|
>
|
||||||
<Input
|
<Input
|
||||||
id="email"
|
id="email"
|
||||||
name="email"
|
name="email"
|
||||||
@ -124,18 +128,17 @@ export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
|
|||||||
value={passwordFormData.email}
|
value={passwordFormData.email}
|
||||||
onChange={(e) => handleFormChange("email", e.target.value)}
|
onChange={(e) => handleFormChange("email", e.target.value)}
|
||||||
placeholder="name@company.com"
|
placeholder="name@company.com"
|
||||||
className="h-[46px] w-full border border-onboarding-border-100 pr-12 placeholder:text-onboarding-text-400"
|
className={`disable-autofill-style h-[46px] w-full placeholder:text-onboarding-text-400 border-0`}
|
||||||
disabled
|
disabled
|
||||||
/>
|
/>
|
||||||
{passwordFormData.email.length > 0 && (
|
{passwordFormData.email.length > 0 && (
|
||||||
<XCircle
|
<div className="flex-shrink-0 h-5 w-5 mr-2 bg-onboarding-background-200 hover:cursor-pointer">
|
||||||
className="absolute right-3 h-5 w-5 stroke-custom-text-400 hover:cursor-pointer"
|
<XCircle className="h-5 w-5 stroke-custom-text-400" onClick={handleEmailClear} />
|
||||||
onClick={handleEmailClear}
|
</div>
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<input type="hidden" value={passwordFormData.email} name="email" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="password">
|
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="password">
|
||||||
{mode === EAuthModes.SIGN_IN ? "Password" : "Set a password"}
|
{mode === EAuthModes.SIGN_IN ? "Password" : "Set a password"}
|
||||||
@ -147,7 +150,7 @@ export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
|
|||||||
value={passwordFormData.password}
|
value={passwordFormData.password}
|
||||||
onChange={(e) => handleFormChange("password", e.target.value)}
|
onChange={(e) => handleFormChange("password", e.target.value)}
|
||||||
placeholder="Enter password"
|
placeholder="Enter password"
|
||||||
className="h-[46px] w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 placeholder:text-onboarding-text-400"
|
className="disable-autofill-style h-[46px] w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 placeholder:text-onboarding-text-400"
|
||||||
onFocus={() => setIsPasswordInputFocused(true)}
|
onFocus={() => setIsPasswordInputFocused(true)}
|
||||||
onBlur={() => setIsPasswordInputFocused(false)}
|
onBlur={() => setIsPasswordInputFocused(false)}
|
||||||
autoFocus
|
autoFocus
|
||||||
@ -166,6 +169,7 @@ export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
{passwordSupport}
|
{passwordSupport}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{mode === EAuthModes.SIGN_UP && (
|
{mode === EAuthModes.SIGN_UP && (
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="confirm_password">
|
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="confirm_password">
|
||||||
@ -178,7 +182,7 @@ export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
|
|||||||
value={passwordFormData.confirm_password}
|
value={passwordFormData.confirm_password}
|
||||||
onChange={(e) => handleFormChange("confirm_password", e.target.value)}
|
onChange={(e) => handleFormChange("confirm_password", e.target.value)}
|
||||||
placeholder="Confirm password"
|
placeholder="Confirm password"
|
||||||
className="h-[46px] w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 placeholder:text-onboarding-text-400"
|
className="disable-autofill-style h-[46px] w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 placeholder:text-onboarding-text-400"
|
||||||
/>
|
/>
|
||||||
{showPassword?.retypePassword ? (
|
{showPassword?.retypePassword ? (
|
||||||
<EyeOff
|
<EyeOff
|
||||||
@ -197,19 +201,20 @@ export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="space-y-2.5">
|
<div className="space-y-2.5">
|
||||||
{mode === EAuthModes.SIGN_IN ? (
|
{mode === EAuthModes.SIGN_IN ? (
|
||||||
<>
|
<>
|
||||||
<Button type="submit" variant="primary" className="w-full" size="lg" disabled={isButtonDisabled}>
|
<Button type="submit" variant="primary" className="w-full" size="lg" disabled={isButtonDisabled}>
|
||||||
{isSubmitting ? (
|
{isSubmitting ? (
|
||||||
<Spinner height="20px" width="20px" />
|
<Spinner height="20px" width="20px" />
|
||||||
) : isSmtpConfigured ? (
|
) : isSMTPConfigured ? (
|
||||||
"Continue"
|
"Continue"
|
||||||
) : (
|
) : (
|
||||||
"Go to workspace"
|
"Go to workspace"
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
{instance && isSmtpConfigured && (
|
{isSMTPConfigured && (
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={redirectToUniqueCodeSignIn}
|
onClick={redirectToUniqueCodeSignIn}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { CircleCheck, XCircle } from "lucide-react";
|
import { CircleCheck, XCircle } from "lucide-react";
|
||||||
import { IEmailCheckData } from "@plane/types";
|
import { Button, Input, Spinner } from "@plane/ui";
|
||||||
import { Button, Input, Spinner, TOAST_TYPE, setToast } from "@plane/ui";
|
|
||||||
// helpers
|
// helpers
|
||||||
import { EAuthModes } from "@/helpers/authentication.helper";
|
import { EAuthModes } from "@/helpers/authentication.helper";
|
||||||
import { API_BASE_URL } from "@/helpers/common.helper";
|
import { API_BASE_URL } from "@/helpers/common.helper";
|
||||||
@ -10,11 +9,14 @@ import useTimer from "@/hooks/use-timer";
|
|||||||
// services
|
// services
|
||||||
import { AuthService } from "@/services/auth.service";
|
import { AuthService } from "@/services/auth.service";
|
||||||
|
|
||||||
type Props = {
|
// services
|
||||||
|
const authService = new AuthService();
|
||||||
|
|
||||||
|
type TAuthUniqueCodeForm = {
|
||||||
|
mode: EAuthModes;
|
||||||
email: string;
|
email: string;
|
||||||
handleEmailClear: () => void;
|
handleEmailClear: () => void;
|
||||||
submitButtonText: string;
|
generateEmailUniqueCode: (email: string) => Promise<{ code: string } | undefined>;
|
||||||
mode: EAuthModes;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type TUniqueCodeFormValues = {
|
type TUniqueCodeFormValues = {
|
||||||
@ -27,55 +29,35 @@ const defaultValues: TUniqueCodeFormValues = {
|
|||||||
code: "",
|
code: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
// services
|
export const AuthUniqueCodeForm: React.FC<TAuthUniqueCodeForm> = (props) => {
|
||||||
const authService = new AuthService();
|
const { mode, email, handleEmailClear, generateEmailUniqueCode } = props;
|
||||||
|
// hooks
|
||||||
export const AuthUniqueCodeForm: React.FC<Props> = (props) => {
|
// const { captureEvent } = useEventTracker();
|
||||||
const { email, handleEmailClear, submitButtonText, mode } = props;
|
// derived values
|
||||||
|
const defaultResetTimerValue = 5;
|
||||||
// states
|
// states
|
||||||
const [uniqueCodeFormData, setUniqueCodeFormData] = useState<TUniqueCodeFormValues>({ ...defaultValues, email });
|
const [uniqueCodeFormData, setUniqueCodeFormData] = useState<TUniqueCodeFormValues>({ ...defaultValues, email });
|
||||||
const [isRequestingNewCode, setIsRequestingNewCode] = useState(false);
|
const [isRequestingNewCode, setIsRequestingNewCode] = useState(false);
|
||||||
const [csrfToken, setCsrfToken] = useState<string | undefined>(undefined);
|
const [csrfToken, setCsrfToken] = useState<string | undefined>(undefined);
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
// store hooks
|
|
||||||
// const { captureEvent } = useEventTracker();
|
|
||||||
// timer
|
// timer
|
||||||
const { timer: resendTimerCode, setTimer: setResendCodeTimer } = useTimer(30);
|
const { timer: resendTimerCode, setTimer: setResendCodeTimer } = useTimer(0);
|
||||||
|
|
||||||
const handleFormChange = (key: keyof TUniqueCodeFormValues, value: string) =>
|
const handleFormChange = (key: keyof TUniqueCodeFormValues, value: string) =>
|
||||||
setUniqueCodeFormData((prev) => ({ ...prev, [key]: value }));
|
setUniqueCodeFormData((prev) => ({ ...prev, [key]: value }));
|
||||||
|
|
||||||
const handleSendNewCode = async (email: string) => {
|
const generateNewCode = async (email: string) => {
|
||||||
const payload: IEmailCheckData = {
|
try {
|
||||||
email,
|
setIsRequestingNewCode(true);
|
||||||
};
|
const uniqueCode = await generateEmailUniqueCode(email);
|
||||||
|
setResendCodeTimer(defaultResetTimerValue);
|
||||||
await authService
|
handleFormChange("code", uniqueCode?.code || "");
|
||||||
.generateUniqueCode(payload)
|
setIsRequestingNewCode(false);
|
||||||
.then(() => {
|
} catch {
|
||||||
setResendCodeTimer(30);
|
setResendCodeTimer(0);
|
||||||
setToast({
|
console.error("Error while requesting new code");
|
||||||
type: TOAST_TYPE.SUCCESS,
|
setIsRequestingNewCode(false);
|
||||||
title: "Success!",
|
}
|
||||||
message: "A new unique code has been sent to your email.",
|
|
||||||
});
|
|
||||||
handleFormChange("code", "");
|
|
||||||
})
|
|
||||||
.catch((err) =>
|
|
||||||
setToast({
|
|
||||||
type: TOAST_TYPE.ERROR,
|
|
||||||
title: "Error!",
|
|
||||||
message: err?.error ?? "Something went wrong while generating unique code. Please try again.",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleRequestNewCode = async (email: string) => {
|
|
||||||
setIsRequestingNewCode(true);
|
|
||||||
|
|
||||||
await handleSendNewCode(email)
|
|
||||||
.then(() => setResendCodeTimer(30))
|
|
||||||
.finally(() => setIsRequestingNewCode(false));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -83,11 +65,6 @@ export const AuthUniqueCodeForm: React.FC<Props> = (props) => {
|
|||||||
authService.requestCSRFToken().then((data) => data?.csrf_token && setCsrfToken(data.csrf_token));
|
authService.requestCSRFToken().then((data) => data?.csrf_token && setCsrfToken(data.csrf_token));
|
||||||
}, [csrfToken]);
|
}, [csrfToken]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
handleRequestNewCode(email);
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const isRequestNewCodeDisabled = isRequestingNewCode || resendTimerCode > 0;
|
const isRequestNewCodeDisabled = isRequestingNewCode || resendTimerCode > 0;
|
||||||
const isButtonDisabled = isRequestingNewCode || !uniqueCodeFormData.code || isSubmitting;
|
const isButtonDisabled = isRequestingNewCode || !uniqueCodeFormData.code || isSubmitting;
|
||||||
|
|
||||||
@ -100,11 +77,14 @@ export const AuthUniqueCodeForm: React.FC<Props> = (props) => {
|
|||||||
onError={() => setIsSubmitting(false)}
|
onError={() => setIsSubmitting(false)}
|
||||||
>
|
>
|
||||||
<input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} />
|
<input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} />
|
||||||
|
<input type="hidden" value={uniqueCodeFormData.email} name="email" />
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<label className="text-sm font-medium text-onboarding-text-300" htmlFor="email">
|
<label className="text-sm font-medium text-onboarding-text-300" htmlFor="email">
|
||||||
Email
|
Email
|
||||||
</label>
|
</label>
|
||||||
<div className="relative flex items-center rounded-md bg-onboarding-background-200">
|
<div
|
||||||
|
className={`relative flex items-center rounded-md bg-onboarding-background-200 border border-onboarding-border-100`}
|
||||||
|
>
|
||||||
<Input
|
<Input
|
||||||
id="email"
|
id="email"
|
||||||
name="email"
|
name="email"
|
||||||
@ -112,18 +92,17 @@ export const AuthUniqueCodeForm: React.FC<Props> = (props) => {
|
|||||||
value={uniqueCodeFormData.email}
|
value={uniqueCodeFormData.email}
|
||||||
onChange={(e) => handleFormChange("email", e.target.value)}
|
onChange={(e) => handleFormChange("email", e.target.value)}
|
||||||
placeholder="name@company.com"
|
placeholder="name@company.com"
|
||||||
className="h-[46px] w-full border border-onboarding-border-100 pr-12 placeholder:text-onboarding-text-400"
|
className={`disable-autofill-style h-[46px] w-full placeholder:text-onboarding-text-400 border-0`}
|
||||||
disabled
|
disabled
|
||||||
/>
|
/>
|
||||||
{uniqueCodeFormData.email.length > 0 && (
|
{uniqueCodeFormData.email.length > 0 && (
|
||||||
<XCircle
|
<div className="flex-shrink-0 h-5 w-5 mr-2 bg-onboarding-background-200 hover:cursor-pointer">
|
||||||
className="absolute right-3 h-5 w-5 stroke-custom-text-400 hover:cursor-pointer"
|
<XCircle className="h-5 w-5 stroke-custom-text-400" onClick={handleEmailClear} />
|
||||||
onClick={handleEmailClear}
|
</div>
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
<input type="hidden" value={uniqueCodeFormData.email} name="email" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<label className="text-sm font-medium text-onboarding-text-300" htmlFor="code">
|
<label className="text-sm font-medium text-onboarding-text-300" htmlFor="code">
|
||||||
Unique code
|
Unique code
|
||||||
@ -132,22 +111,18 @@ export const AuthUniqueCodeForm: React.FC<Props> = (props) => {
|
|||||||
name="code"
|
name="code"
|
||||||
value={uniqueCodeFormData.code}
|
value={uniqueCodeFormData.code}
|
||||||
onChange={(e) => handleFormChange("code", e.target.value)}
|
onChange={(e) => handleFormChange("code", e.target.value)}
|
||||||
// FIXME:
|
|
||||||
// hasError={Boolean(errors.code)}
|
|
||||||
placeholder="gets-sets-flys"
|
placeholder="gets-sets-flys"
|
||||||
className="h-[46px] w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 placeholder:text-onboarding-text-400"
|
className="disable-autofill-style h-[46px] w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 placeholder:text-onboarding-text-400"
|
||||||
autoFocus
|
autoFocus
|
||||||
/>
|
/>
|
||||||
{/* )}
|
<div className="flex w-full items-center justify-between px-1 text-xs pt-1">
|
||||||
/> */}
|
|
||||||
<div className="flex w-full items-center justify-between px-1 text-xs">
|
|
||||||
<p className="flex items-center gap-1 font-medium text-green-700">
|
<p className="flex items-center gap-1 font-medium text-green-700">
|
||||||
<CircleCheck height={12} width={12} />
|
<CircleCheck height={12} width={12} />
|
||||||
Paste the code sent to your email
|
Paste the code sent to your email
|
||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => handleRequestNewCode(uniqueCodeFormData.email)}
|
onClick={() => generateNewCode(uniqueCodeFormData.email)}
|
||||||
className={`${
|
className={`${
|
||||||
isRequestNewCodeDisabled
|
isRequestNewCodeDisabled
|
||||||
? "text-onboarding-text-400"
|
? "text-onboarding-text-400"
|
||||||
@ -163,15 +138,12 @@ export const AuthUniqueCodeForm: React.FC<Props> = (props) => {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Button type="submit" variant="primary" className="w-full" size="lg" disabled={isButtonDisabled}>
|
|
||||||
{isRequestingNewCode ? (
|
<div className="space-y-2.5">
|
||||||
"Sending code"
|
<Button type="submit" variant="primary" className="w-full" size="lg" disabled={isButtonDisabled}>
|
||||||
) : isSubmitting ? (
|
{isRequestingNewCode ? "Sending code" : isSubmitting ? <Spinner height="20px" width="20px" /> : "Continue"}
|
||||||
<Spinner height="20px" width="20px" />
|
</Button>
|
||||||
) : (
|
</div>
|
||||||
submitButtonText
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -24,9 +24,9 @@ export const PasswordStrengthMeter: React.FC<Props> = (props: Props) => {
|
|||||||
text = "Password is too short";
|
text = "Password is too short";
|
||||||
textColor = `text-[#DC3E42]`;
|
textColor = `text-[#DC3E42]`;
|
||||||
} else if (strength < 3) {
|
} else if (strength < 3) {
|
||||||
bars = [`bg-[#FFBA18]`, `bg-[#FFBA18]`, `bg-[#F0F0F3]`];
|
bars = [`bg-[#DC3E42]`, `bg-[#F0F0F3]`, `bg-[#F0F0F3]`];
|
||||||
text = "Password is weak";
|
text = "Password is weak";
|
||||||
textColor = `text-[#FFBA18]`;
|
textColor = `text-[#F0F0F3]`;
|
||||||
} else {
|
} else {
|
||||||
bars = [`bg-[#3E9B4F]`, `bg-[#3E9B4F]`, `bg-[#3E9B4F]`];
|
bars = [`bg-[#3E9B4F]`, `bg-[#3E9B4F]`, `bg-[#3E9B4F]`];
|
||||||
text = "Password is strong";
|
text = "Password is strong";
|
||||||
|
@ -148,7 +148,10 @@ export const CreateWorkspace: React.FC<Props> = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
<form className="w-full mx-auto mt-2 space-y-4" onSubmit={handleSubmit(handleCreateWorkspace)}>
|
<form className="w-full mx-auto mt-2 space-y-4" onSubmit={handleSubmit(handleCreateWorkspace)}>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="name">
|
<label
|
||||||
|
className="text-sm text-onboarding-text-300 font-medium after:content-['*'] after:ml-0.5 after:text-red-500"
|
||||||
|
htmlFor="name"
|
||||||
|
>
|
||||||
Workspace name
|
Workspace name
|
||||||
</label>
|
</label>
|
||||||
<Controller
|
<Controller
|
||||||
@ -187,7 +190,10 @@ export const CreateWorkspace: React.FC<Props> = (props) => {
|
|||||||
{errors.name && <span className="text-sm text-red-500">{errors.name.message}</span>}
|
{errors.name && <span className="text-sm text-red-500">{errors.name.message}</span>}
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="slug">
|
<label
|
||||||
|
className="text-sm text-onboarding-text-300 font-medium after:content-['*'] after:ml-0.5 after:text-red-500"
|
||||||
|
htmlFor="slug"
|
||||||
|
>
|
||||||
Workspace URL
|
Workspace URL
|
||||||
</label>
|
</label>
|
||||||
<Controller
|
<Controller
|
||||||
@ -224,7 +230,10 @@ export const CreateWorkspace: React.FC<Props> = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
<hr className="w-full border-onboarding-border-100" />
|
<hr className="w-full border-onboarding-border-100" />
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="organization_size">
|
<label
|
||||||
|
className="text-sm text-onboarding-text-300 font-medium after:content-['*'] after:ml-0.5 after:text-red-500"
|
||||||
|
htmlFor="organization_size"
|
||||||
|
>
|
||||||
Company size
|
Company size
|
||||||
</label>
|
</label>
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
|
@ -157,6 +157,7 @@ const InviteMemberInput: React.FC<InviteMemberFormProps> = (props) => {
|
|||||||
hasError={Boolean(errors.emails?.[index]?.email)}
|
hasError={Boolean(errors.emails?.[index]?.email)}
|
||||||
placeholder={placeholderEmails[index % placeholderEmails.length]}
|
placeholder={placeholderEmails[index % placeholderEmails.length]}
|
||||||
className="w-full border-onboarding-border-100 text-xs placeholder:text-onboarding-text-400 sm:text-sm"
|
className="w-full border-onboarding-border-100 text-xs placeholder:text-onboarding-text-400 sm:text-sm"
|
||||||
|
autoComplete="off"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
@ -333,7 +333,10 @@ export const ProfileSetup: React.FC<Props> = observer((props) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="first_name">
|
<label
|
||||||
|
className="text-sm text-onboarding-text-300 font-medium after:content-['*'] after:ml-0.5 after:text-red-500"
|
||||||
|
htmlFor="first_name"
|
||||||
|
>
|
||||||
First name
|
First name
|
||||||
</label>
|
</label>
|
||||||
<Controller
|
<Controller
|
||||||
@ -364,7 +367,10 @@ export const ProfileSetup: React.FC<Props> = observer((props) => {
|
|||||||
{errors.first_name && <span className="text-sm text-red-500">{errors.first_name.message}</span>}
|
{errors.first_name && <span className="text-sm text-red-500">{errors.first_name.message}</span>}
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="last_name">
|
<label
|
||||||
|
className="text-sm text-onboarding-text-300 font-medium after:content-['*'] after:ml-0.5 after:text-red-500"
|
||||||
|
htmlFor="last_name"
|
||||||
|
>
|
||||||
Last name
|
Last name
|
||||||
</label>
|
</label>
|
||||||
<Controller
|
<Controller
|
||||||
@ -485,7 +491,10 @@ export const ProfileSetup: React.FC<Props> = observer((props) => {
|
|||||||
{profileSetupStep !== EProfileSetupSteps.USER_DETAILS && (
|
{profileSetupStep !== EProfileSetupSteps.USER_DETAILS && (
|
||||||
<>
|
<>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="role">
|
<label
|
||||||
|
className="text-sm text-onboarding-text-300 font-medium after:content-['*'] after:ml-0.5 after:text-red-500"
|
||||||
|
htmlFor="role"
|
||||||
|
>
|
||||||
What role are you working on? Choose one.
|
What role are you working on? Choose one.
|
||||||
</label>
|
</label>
|
||||||
<Controller
|
<Controller
|
||||||
@ -513,7 +522,10 @@ export const ProfileSetup: React.FC<Props> = observer((props) => {
|
|||||||
{errors.role && <span className="text-sm text-red-500">{errors.role.message}</span>}
|
{errors.role && <span className="text-sm text-red-500">{errors.role.message}</span>}
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="use_case">
|
<label
|
||||||
|
className="text-sm text-onboarding-text-300 font-medium after:content-['*'] after:ml-0.5 after:text-red-500"
|
||||||
|
htmlFor="use_case"
|
||||||
|
>
|
||||||
What is your domain expertise? Choose one.
|
What is your domain expertise? Choose one.
|
||||||
</label>
|
</label>
|
||||||
<Controller
|
<Controller
|
||||||
|
@ -22,7 +22,6 @@ export enum EAuthSteps {
|
|||||||
|
|
||||||
export enum EErrorAlertType {
|
export enum EErrorAlertType {
|
||||||
BANNER_ALERT = "BANNER_ALERT",
|
BANNER_ALERT = "BANNER_ALERT",
|
||||||
TOAST_ALERT = "TOAST_ALERT",
|
|
||||||
INLINE_FIRST_NAME = "INLINE_FIRST_NAME",
|
INLINE_FIRST_NAME = "INLINE_FIRST_NAME",
|
||||||
INLINE_EMAIL = "INLINE_EMAIL",
|
INLINE_EMAIL = "INLINE_EMAIL",
|
||||||
INLINE_PASSWORD = "INLINE_PASSWORD",
|
INLINE_PASSWORD = "INLINE_PASSWORD",
|
||||||
@ -32,42 +31,52 @@ export enum EErrorAlertType {
|
|||||||
export enum EAuthenticationErrorCodes {
|
export enum EAuthenticationErrorCodes {
|
||||||
// Global
|
// Global
|
||||||
INSTANCE_NOT_CONFIGURED = "5000",
|
INSTANCE_NOT_CONFIGURED = "5000",
|
||||||
SIGNUP_DISABLED = "5001",
|
INVALID_EMAIL = "5005",
|
||||||
INVALID_PASSWORD = "5002", // Password strength validation
|
EMAIL_REQUIRED = "5010",
|
||||||
SMTP_NOT_CONFIGURED = "5007",
|
SIGNUP_DISABLED = "5015",
|
||||||
// email check
|
// Password strength
|
||||||
INVALID_EMAIL = "5012",
|
INVALID_PASSWORD = "5020",
|
||||||
EMAIL_REQUIRED = "5013",
|
SMTP_NOT_CONFIGURED = "5025",
|
||||||
// Sign Up
|
// Sign Up
|
||||||
USER_ALREADY_EXIST = "5003",
|
USER_ALREADY_EXIST = "5030",
|
||||||
REQUIRED_EMAIL_PASSWORD_SIGN_UP = "5015",
|
AUTHENTICATION_FAILED_SIGN_UP = "5035",
|
||||||
AUTHENTICATION_FAILED_SIGN_UP = "5006",
|
REQUIRED_EMAIL_PASSWORD_SIGN_UP = "5040",
|
||||||
INVALID_EMAIL_SIGN_UP = "5017",
|
INVALID_EMAIL_SIGN_UP = "5045",
|
||||||
MAGIC_SIGN_UP_EMAIL_CODE_REQUIRED = "5023",
|
INVALID_EMAIL_MAGIC_SIGN_UP = "5050",
|
||||||
INVALID_EMAIL_MAGIC_SIGN_UP = "5019",
|
MAGIC_SIGN_UP_EMAIL_CODE_REQUIRED = "5055",
|
||||||
// Sign In
|
// Sign In
|
||||||
USER_DOES_NOT_EXIST = "5004",
|
USER_DOES_NOT_EXIST = "5060",
|
||||||
REQUIRED_EMAIL_PASSWORD_SIGN_IN = "5014",
|
AUTHENTICATION_FAILED_SIGN_IN = "5065",
|
||||||
AUTHENTICATION_FAILED_SIGN_IN = "5005",
|
REQUIRED_EMAIL_PASSWORD_SIGN_IN = "5070",
|
||||||
INVALID_EMAIL_SIGN_IN = "5016",
|
INVALID_EMAIL_SIGN_IN = "5075",
|
||||||
MAGIC_SIGN_IN_EMAIL_CODE_REQUIRED = "5022",
|
INVALID_EMAIL_MAGIC_SIGN_IN = "5080",
|
||||||
INVALID_EMAIL_MAGIC_SIGN_IN = "5018",
|
MAGIC_SIGN_IN_EMAIL_CODE_REQUIRED = "5085",
|
||||||
// Both Sign in and Sign up
|
// Both Sign in and Sign up for magic
|
||||||
INVALID_MAGIC_CODE = "5008",
|
INVALID_MAGIC_CODE = "5090",
|
||||||
EXPIRED_MAGIC_CODE = "5009",
|
EXPIRED_MAGIC_CODE = "5095",
|
||||||
|
EMAIL_CODE_ATTEMPT_EXHAUSTED = "5100",
|
||||||
// Oauth
|
// Oauth
|
||||||
GOOGLE_NOT_CONFIGURED = "5010",
|
GOOGLE_NOT_CONFIGURED = "5105",
|
||||||
GITHUB_NOT_CONFIGURED = "5011",
|
GITHUB_NOT_CONFIGURED = "5110",
|
||||||
GOOGLE_OAUTH_PROVIDER_ERROR = "5021",
|
GOOGLE_OAUTH_PROVIDER_ERROR = "5115",
|
||||||
GITHUB_OAUTH_PROVIDER_ERROR = "5020",
|
GITHUB_OAUTH_PROVIDER_ERROR = "5120",
|
||||||
// Reset Password
|
// Reset Password
|
||||||
INVALID_PASSWORD_TOKEN = "5024",
|
INVALID_PASSWORD_TOKEN = "5125",
|
||||||
EXPIRED_PASSWORD_TOKEN = "5025",
|
EXPIRED_PASSWORD_TOKEN = "5130",
|
||||||
// Change password
|
// Change password
|
||||||
INCORRECT_OLD_PASSWORD = "5026",
|
INCORRECT_OLD_PASSWORD = "5135",
|
||||||
INVALID_NEW_PASSWORD = "5027",
|
INVALID_NEW_PASSWORD = "5140",
|
||||||
// set password
|
// set passowrd
|
||||||
PASSWORD_ALREADY_SET = "5028", // used in the onboarding and set password page
|
PASSWORD_ALREADY_SET = "5145",
|
||||||
|
// Admin
|
||||||
|
ADMIN_ALREADY_EXIST = "5150",
|
||||||
|
REQUIRED_ADMIN_EMAIL_PASSWORD_FIRST_NAME = "5155",
|
||||||
|
INVALID_ADMIN_EMAIL = "5160",
|
||||||
|
INVALID_ADMIN_PASSWORD = "5165",
|
||||||
|
REQUIRED_ADMIN_EMAIL_PASSWORD = "5170",
|
||||||
|
ADMIN_AUTHENTICATION_FAILED = "5175",
|
||||||
|
ADMIN_USER_ALREADY_EXIST = "5180",
|
||||||
|
ADMIN_USER_DOES_NOT_EXIST = "5185",
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TAuthErrorInfo = {
|
export type TAuthErrorInfo = {
|
||||||
@ -116,7 +125,7 @@ const errorCodeMessages: {
|
|||||||
Your account is already registered.
|
Your account is already registered.
|
||||||
<Link
|
<Link
|
||||||
className="underline underline-offset-4 font-medium hover:font-bold transition-all"
|
className="underline underline-offset-4 font-medium hover:font-bold transition-all"
|
||||||
href={`/accounts/sign-in${email ? `?email=${email}` : ``}`}
|
href={`/sign-in${email ? `?email=${email}` : ``}`}
|
||||||
>
|
>
|
||||||
Sign In
|
Sign In
|
||||||
</Link>
|
</Link>
|
||||||
@ -191,6 +200,10 @@ const errorCodeMessages: {
|
|||||||
title: `Expired magic code`,
|
title: `Expired magic code`,
|
||||||
message: () => `Expired magic code. Please try again.`,
|
message: () => `Expired magic code. Please try again.`,
|
||||||
},
|
},
|
||||||
|
[EAuthenticationErrorCodes.EMAIL_CODE_ATTEMPT_EXHAUSTED]: {
|
||||||
|
title: `Expired magic code`,
|
||||||
|
message: () => `Expired magic code. Please try again.`,
|
||||||
|
},
|
||||||
|
|
||||||
// Oauth
|
// Oauth
|
||||||
[EAuthenticationErrorCodes.GOOGLE_NOT_CONFIGURED]: {
|
[EAuthenticationErrorCodes.GOOGLE_NOT_CONFIGURED]: {
|
||||||
@ -235,27 +248,84 @@ const errorCodeMessages: {
|
|||||||
title: `Password already set`,
|
title: `Password already set`,
|
||||||
message: () => `Password already set. Please try again.`,
|
message: () => `Password already set. Please try again.`,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// admin
|
||||||
|
[EAuthenticationErrorCodes.ADMIN_ALREADY_EXIST]: {
|
||||||
|
title: `Admin already exists`,
|
||||||
|
message: () => `Admin already exists. Please try again.`,
|
||||||
|
},
|
||||||
|
[EAuthenticationErrorCodes.REQUIRED_ADMIN_EMAIL_PASSWORD_FIRST_NAME]: {
|
||||||
|
title: `Email, password and first name required`,
|
||||||
|
message: () => `Email, password and first name required. Please try again.`,
|
||||||
|
},
|
||||||
|
[EAuthenticationErrorCodes.INVALID_ADMIN_EMAIL]: {
|
||||||
|
title: `Invalid admin email`,
|
||||||
|
message: () => `Invalid admin email. Please try again.`,
|
||||||
|
},
|
||||||
|
[EAuthenticationErrorCodes.INVALID_ADMIN_PASSWORD]: {
|
||||||
|
title: `Invalid admin password`,
|
||||||
|
message: () => `Invalid admin password. Please try again.`,
|
||||||
|
},
|
||||||
|
[EAuthenticationErrorCodes.REQUIRED_ADMIN_EMAIL_PASSWORD]: {
|
||||||
|
title: `Email and password required`,
|
||||||
|
message: () => `Email and password required. Please try again.`,
|
||||||
|
},
|
||||||
|
[EAuthenticationErrorCodes.ADMIN_AUTHENTICATION_FAILED]: {
|
||||||
|
title: `Authentication failed`,
|
||||||
|
message: () => `Authentication failed. Please try again.`,
|
||||||
|
},
|
||||||
|
[EAuthenticationErrorCodes.ADMIN_USER_ALREADY_EXIST]: {
|
||||||
|
title: `Admin user already exists`,
|
||||||
|
message: () => (
|
||||||
|
<div>
|
||||||
|
Admin user already exists.
|
||||||
|
<Link className="underline underline-offset-4 font-medium hover:font-bold transition-all" href={`/admin`}>
|
||||||
|
Sign In
|
||||||
|
</Link>
|
||||||
|
now.
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
[EAuthenticationErrorCodes.ADMIN_USER_DOES_NOT_EXIST]: {
|
||||||
|
title: `Admin user does not exist`,
|
||||||
|
message: () => (
|
||||||
|
<div>
|
||||||
|
Admin user does not exist.
|
||||||
|
<Link className="underline underline-offset-4 font-medium hover:font-bold transition-all" href={`/admin`}>
|
||||||
|
Sign In
|
||||||
|
</Link>
|
||||||
|
now.
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const authErrorHandler = (
|
export const authErrorHandler = (
|
||||||
errorCode: EAuthenticationErrorCodes,
|
errorCode: EAuthenticationErrorCodes,
|
||||||
email?: string | undefined
|
email?: string | undefined
|
||||||
): TAuthErrorInfo | undefined => {
|
): TAuthErrorInfo | undefined => {
|
||||||
const toastAlertErrorCodes = [
|
const bannerAlertErrorCodes = [
|
||||||
|
EAuthenticationErrorCodes.INSTANCE_NOT_CONFIGURED,
|
||||||
|
EAuthenticationErrorCodes.INVALID_EMAIL,
|
||||||
|
EAuthenticationErrorCodes.EMAIL_REQUIRED,
|
||||||
EAuthenticationErrorCodes.SIGNUP_DISABLED,
|
EAuthenticationErrorCodes.SIGNUP_DISABLED,
|
||||||
EAuthenticationErrorCodes.INVALID_PASSWORD,
|
EAuthenticationErrorCodes.INVALID_PASSWORD,
|
||||||
EAuthenticationErrorCodes.SMTP_NOT_CONFIGURED,
|
EAuthenticationErrorCodes.SMTP_NOT_CONFIGURED,
|
||||||
EAuthenticationErrorCodes.INVALID_EMAIL,
|
EAuthenticationErrorCodes.USER_ALREADY_EXIST,
|
||||||
EAuthenticationErrorCodes.EMAIL_REQUIRED,
|
|
||||||
EAuthenticationErrorCodes.AUTHENTICATION_FAILED_SIGN_UP,
|
EAuthenticationErrorCodes.AUTHENTICATION_FAILED_SIGN_UP,
|
||||||
|
EAuthenticationErrorCodes.REQUIRED_EMAIL_PASSWORD_SIGN_UP,
|
||||||
EAuthenticationErrorCodes.INVALID_EMAIL_SIGN_UP,
|
EAuthenticationErrorCodes.INVALID_EMAIL_SIGN_UP,
|
||||||
EAuthenticationErrorCodes.MAGIC_SIGN_UP_EMAIL_CODE_REQUIRED,
|
|
||||||
EAuthenticationErrorCodes.INVALID_EMAIL_MAGIC_SIGN_UP,
|
EAuthenticationErrorCodes.INVALID_EMAIL_MAGIC_SIGN_UP,
|
||||||
|
EAuthenticationErrorCodes.MAGIC_SIGN_UP_EMAIL_CODE_REQUIRED,
|
||||||
|
EAuthenticationErrorCodes.USER_DOES_NOT_EXIST,
|
||||||
EAuthenticationErrorCodes.AUTHENTICATION_FAILED_SIGN_IN,
|
EAuthenticationErrorCodes.AUTHENTICATION_FAILED_SIGN_IN,
|
||||||
|
EAuthenticationErrorCodes.REQUIRED_EMAIL_PASSWORD_SIGN_IN,
|
||||||
EAuthenticationErrorCodes.INVALID_EMAIL_SIGN_IN,
|
EAuthenticationErrorCodes.INVALID_EMAIL_SIGN_IN,
|
||||||
EAuthenticationErrorCodes.INVALID_EMAIL_MAGIC_SIGN_IN,
|
EAuthenticationErrorCodes.INVALID_EMAIL_MAGIC_SIGN_IN,
|
||||||
|
EAuthenticationErrorCodes.MAGIC_SIGN_IN_EMAIL_CODE_REQUIRED,
|
||||||
EAuthenticationErrorCodes.INVALID_MAGIC_CODE,
|
EAuthenticationErrorCodes.INVALID_MAGIC_CODE,
|
||||||
EAuthenticationErrorCodes.EXPIRED_MAGIC_CODE,
|
EAuthenticationErrorCodes.EXPIRED_MAGIC_CODE,
|
||||||
|
EAuthenticationErrorCodes.EMAIL_CODE_ATTEMPT_EXHAUSTED,
|
||||||
EAuthenticationErrorCodes.GOOGLE_NOT_CONFIGURED,
|
EAuthenticationErrorCodes.GOOGLE_NOT_CONFIGURED,
|
||||||
EAuthenticationErrorCodes.GITHUB_NOT_CONFIGURED,
|
EAuthenticationErrorCodes.GITHUB_NOT_CONFIGURED,
|
||||||
EAuthenticationErrorCodes.GOOGLE_OAUTH_PROVIDER_ERROR,
|
EAuthenticationErrorCodes.GOOGLE_OAUTH_PROVIDER_ERROR,
|
||||||
@ -265,23 +335,15 @@ export const authErrorHandler = (
|
|||||||
EAuthenticationErrorCodes.INCORRECT_OLD_PASSWORD,
|
EAuthenticationErrorCodes.INCORRECT_OLD_PASSWORD,
|
||||||
EAuthenticationErrorCodes.INVALID_NEW_PASSWORD,
|
EAuthenticationErrorCodes.INVALID_NEW_PASSWORD,
|
||||||
EAuthenticationErrorCodes.PASSWORD_ALREADY_SET,
|
EAuthenticationErrorCodes.PASSWORD_ALREADY_SET,
|
||||||
|
EAuthenticationErrorCodes.ADMIN_ALREADY_EXIST,
|
||||||
|
EAuthenticationErrorCodes.REQUIRED_ADMIN_EMAIL_PASSWORD_FIRST_NAME,
|
||||||
|
EAuthenticationErrorCodes.INVALID_ADMIN_EMAIL,
|
||||||
|
EAuthenticationErrorCodes.INVALID_ADMIN_PASSWORD,
|
||||||
|
EAuthenticationErrorCodes.REQUIRED_ADMIN_EMAIL_PASSWORD,
|
||||||
|
EAuthenticationErrorCodes.ADMIN_AUTHENTICATION_FAILED,
|
||||||
|
EAuthenticationErrorCodes.ADMIN_USER_ALREADY_EXIST,
|
||||||
|
EAuthenticationErrorCodes.ADMIN_USER_DOES_NOT_EXIST,
|
||||||
];
|
];
|
||||||
const bannerAlertErrorCodes = [
|
|
||||||
EAuthenticationErrorCodes.INSTANCE_NOT_CONFIGURED,
|
|
||||||
EAuthenticationErrorCodes.USER_ALREADY_EXIST,
|
|
||||||
EAuthenticationErrorCodes.USER_DOES_NOT_EXIST,
|
|
||||||
EAuthenticationErrorCodes.REQUIRED_EMAIL_PASSWORD_SIGN_UP,
|
|
||||||
EAuthenticationErrorCodes.REQUIRED_EMAIL_PASSWORD_SIGN_IN,
|
|
||||||
EAuthenticationErrorCodes.MAGIC_SIGN_IN_EMAIL_CODE_REQUIRED,
|
|
||||||
];
|
|
||||||
|
|
||||||
if (toastAlertErrorCodes.includes(errorCode))
|
|
||||||
return {
|
|
||||||
type: EErrorAlertType.TOAST_ALERT,
|
|
||||||
code: errorCode,
|
|
||||||
title: errorCodeMessages[errorCode]?.title || "Error",
|
|
||||||
message: errorCodeMessages[errorCode]?.message(email) || "Something went wrong. Please try again.",
|
|
||||||
};
|
|
||||||
|
|
||||||
if (bannerAlertErrorCodes.includes(errorCode))
|
if (bannerAlertErrorCodes.includes(errorCode))
|
||||||
return {
|
return {
|
||||||
|
@ -95,7 +95,7 @@ export const WorkspaceAuthWrapper: FC<IWorkspaceAuthWrapper> = observer((props)
|
|||||||
membership.hasPermissionToCurrentWorkspace === undefined
|
membership.hasPermissionToCurrentWorkspace === undefined
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<div className="relative flex h-full w-full flex-col items-center justify-center bg-custom-background-90">
|
<div className="relative flex h-screen w-full flex-col items-center justify-center bg-custom-background-90 ">
|
||||||
<div className="container relative mx-auto flex h-full w-full flex-col overflow-hidden overflow-y-auto px-5 py-14 md:px-0">
|
<div className="container relative mx-auto flex h-full w-full flex-col overflow-hidden overflow-y-auto px-5 py-14 md:px-0">
|
||||||
<div className="relative flex flex-shrink-0 items-center justify-between gap-4">
|
<div className="relative flex flex-shrink-0 items-center justify-between gap-4">
|
||||||
<div className="z-10 flex-shrink-0 bg-custom-background-90 py-4">
|
<div className="z-10 flex-shrink-0 bg-custom-background-90 py-4">
|
||||||
|
@ -84,7 +84,7 @@ export const AuthenticationWrapper: FC<TAuthenticationWrapper> = observer((props
|
|||||||
|
|
||||||
if (pageType === EPageTypes.ONBOARDING) {
|
if (pageType === EPageTypes.ONBOARDING) {
|
||||||
if (!currentUser?.id) {
|
if (!currentUser?.id) {
|
||||||
router.push("/accounts/sign-in");
|
router.push("/sign-in");
|
||||||
return <></>;
|
return <></>;
|
||||||
} else {
|
} else {
|
||||||
if (currentUser && currentUserProfile?.id && currentUserProfile?.is_onboarded) {
|
if (currentUser && currentUserProfile?.id && currentUserProfile?.is_onboarded) {
|
||||||
@ -97,7 +97,7 @@ export const AuthenticationWrapper: FC<TAuthenticationWrapper> = observer((props
|
|||||||
|
|
||||||
if (pageType === EPageTypes.SET_PASSWORD) {
|
if (pageType === EPageTypes.SET_PASSWORD) {
|
||||||
if (!currentUser?.id) {
|
if (!currentUser?.id) {
|
||||||
router.push("/accounts/sign-in");
|
router.push("/sign-in");
|
||||||
return <></>;
|
return <></>;
|
||||||
} else {
|
} else {
|
||||||
if (
|
if (
|
||||||
@ -121,7 +121,7 @@ export const AuthenticationWrapper: FC<TAuthenticationWrapper> = observer((props
|
|||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
router.push("/accounts/sign-in");
|
router.push("/sign-in");
|
||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,7 +176,7 @@ const ForgotPasswordPage: NextPageWithLayout = () => {
|
|||||||
>
|
>
|
||||||
{resendTimerCode > 0 ? `Resend in ${resendTimerCode} seconds` : "Send reset link"}
|
{resendTimerCode > 0 ? `Resend in ${resendTimerCode} seconds` : "Send reset link"}
|
||||||
</Button>
|
</Button>
|
||||||
<Link href="/accounts/sign-in" className={cn("w-full", getButtonStyling("link-neutral", "lg"))}>
|
<Link href="/sign-in" className={cn("w-full", getButtonStyling("link-neutral", "lg"))}>
|
||||||
Back to sign in
|
Back to sign in
|
||||||
</Link>
|
</Link>
|
||||||
</form>
|
</form>
|
||||||
|
@ -48,7 +48,7 @@ const HomePage: NextPageWithLayout = observer(() => {
|
|||||||
<div className="text-center text-sm font-medium text-onboarding-text-300">
|
<div className="text-center text-sm font-medium text-onboarding-text-300">
|
||||||
Already have an account?{" "}
|
Already have an account?{" "}
|
||||||
<Link
|
<Link
|
||||||
href="/accounts/sign-in"
|
href="/sign-in"
|
||||||
onClick={() => captureEvent(NAVIGATE_TO_SIGNIN, {})}
|
onClick={() => captureEvent(NAVIGATE_TO_SIGNIN, {})}
|
||||||
className="font-semibold text-custom-primary-100 hover:underline"
|
className="font-semibold text-custom-primary-100 hover:underline"
|
||||||
>
|
>
|
||||||
|
@ -640,4 +640,13 @@ div.web-view-spinner div.bar12 {
|
|||||||
.highlight-with-line {
|
.highlight-with-line {
|
||||||
border-left: 5px solid rgb(var(--color-primary-100)) !important;
|
border-left: 5px solid rgb(var(--color-primary-100)) !important;
|
||||||
background: rgb(var(--color-background-80));
|
background: rgb(var(--color-background-80));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* By applying below class, the autofilled text in form fields will not have the default autofill background color and styles applied by WebKit browsers */
|
||||||
|
|
||||||
|
.disable-autofill-style:-webkit-autofill,
|
||||||
|
.disable-autofill-style:-webkit-autofill:hover,
|
||||||
|
.disable-autofill-style:-webkit-autofill:focus,
|
||||||
|
.disable-autofill-style:-webkit-autofill:active {
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user