chore: Updating the login and signup page for cloud hosting as per new design (#32641)
## Description Updating the login and signup page for cloud hosting as per new design Fixes [#32267](https://github.com/appsmithorg/appsmith/issues/32267) ## Automation /ok-to-test tags="@tag.All" ### 🔍 Cypress test results <!-- This is an auto-generated comment: Cypress test results --> > [!TIP] > 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉 > Workflow run: <https://github.com/appsmithorg/appsmith/actions/runs/8723701550> > Commit: 65a0179c5d22e1f950888c24a119af608aed2a28 > Cypress dashboard url: <a href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=8723701550&attempt=4" target="_blank">Click here!</a> <!-- end of auto-generated comment: Cypress test results --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Enhanced titles and messages across login, signup, and forgot password pages for clarity and engagement. - Added new analytics event for tracking visits to self-hosting documentation. - Introduced new content and layout adjustments in user authentication pages to improve user experience. - Implemented conditional rendering to optimize content display for mobile devices and cloud hosting scenarios. - **Bug Fixes** - Updated footer links to use consistent capitalization. - **Refactor** - Major structural and styling overhaul of forms and user authentication components to utilize modern CSS practices and improve maintainability. - **Documentation** - Added direct links to self-hosting documentation to facilitate user access. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
parent
313338899e
commit
e07e3ecd04
|
|
@ -64,7 +64,7 @@ describe(
|
|||
homePage.Signout();
|
||||
// validating sso with github is enabled
|
||||
assertHelper.AssertContains(
|
||||
"Continue with Github",
|
||||
"Github",
|
||||
"be.visible",
|
||||
adminSettingsLocators.loginWithGithub,
|
||||
);
|
||||
|
|
@ -110,7 +110,7 @@ describe(
|
|||
homePage.Signout();
|
||||
// validating sso with github is disabled
|
||||
assertHelper.AssertContains(
|
||||
"Continue with Github",
|
||||
"Github",
|
||||
"not.exist",
|
||||
adminSettingsLocators.loginWithGithub,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -61,10 +61,7 @@ describe(
|
|||
cy.get(homePage.signOutIcon).click();
|
||||
cy.wait(500);
|
||||
// validating sso with google is enabled
|
||||
cy.get(adminSettings.loginWithGoogle).should(
|
||||
"have.text",
|
||||
"Continue with Google",
|
||||
);
|
||||
cy.get(adminSettings.loginWithGoogle).should("have.text", "Google");
|
||||
});
|
||||
|
||||
it("3. Go to admin settings and disable Google", function () {
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@
|
|||
"d3-geo": "^3.1.0",
|
||||
"dayjs": "^1.10.6",
|
||||
"deep-diff": "^1.0.2",
|
||||
"design-system": "npm:@appsmithorg/design-system@2.1.36",
|
||||
"design-system": "npm:@appsmithorg/design-system@2.1.37",
|
||||
"design-system-old": "npm:@appsmithorg/design-system-old@1.1.16",
|
||||
"downloadjs": "^1.4.7",
|
||||
"echarts": "^5.4.2",
|
||||
|
|
|
|||
|
|
@ -57,8 +57,7 @@ export const FORM_VALIDATION_INVALID_PASSWORD = FORM_VALIDATION_PASSWORD_RULE;
|
|||
|
||||
export const LOGIN_PAGE_EMAIL_INPUT_LABEL = () => `Email`;
|
||||
export const LOGIN_PAGE_PASSWORD_INPUT_LABEL = () => `Password`;
|
||||
export const LOGIN_PAGE_EMAIL_INPUT_PLACEHOLDER = () =>
|
||||
`Enter your email address`;
|
||||
export const LOGIN_PAGE_EMAIL_INPUT_PLACEHOLDER = () => `Enter your email`;
|
||||
export const LOGIN_PAGE_PASSWORD_INPUT_PLACEHOLDER = () =>
|
||||
`Enter your password`;
|
||||
export const LOGIN_PAGE_INVALID_CREDS_ERROR = () =>
|
||||
|
|
@ -66,25 +65,28 @@ export const LOGIN_PAGE_INVALID_CREDS_ERROR = () =>
|
|||
export const LOGIN_PAGE_INVALID_CREDS_FORGOT_PASSWORD_LINK = () =>
|
||||
`Reset password`;
|
||||
export const NEW_TO_APPSMITH = () => `Don't have an account?`;
|
||||
export const LOGIN_PAGE_TITLE = () => `Sign in`;
|
||||
export const LOGIN_PAGE_TITLE = () => `Sign in to your account`;
|
||||
export const LOGIN_PAGE_SUBTITLE = () => `Sign in to your account`;
|
||||
|
||||
export const LOGIN_PAGE_LOGIN_BUTTON_TEXT = () => `Sign in`;
|
||||
export const LOGIN_PAGE_FORGOT_PASSWORD_TEXT = () => `Forgot password`;
|
||||
export const LOGIN_PAGE_REMEMBER_ME_LABEL = () => `Remember`;
|
||||
export const LOGIN_PAGE_SIGN_UP_LINK_TEXT = () => `Sign up`;
|
||||
export const SIGNUP_PAGE_TITLE = () => `Create your free account`;
|
||||
export const SIGNUP_PAGE_TITLE = () => `Create your account`;
|
||||
export const SIGNUP_PAGE_SUBTITLE = () => `Use your workspace email`;
|
||||
export const SIGNUP_PAGE_EMAIL_INPUT_LABEL = () => `Email`;
|
||||
export const SIGNUP_PAGE_EMAIL_INPUT_PLACEHOLDER = () => `Email`;
|
||||
export const SIGNUP_PAGE_EMAIL_INPUT_PLACEHOLDER = () => `Enter your email`;
|
||||
export const SIGNUP_PAGE_NAME_INPUT_PLACEHOLDER = () => `Name`;
|
||||
export const SIGNUP_PAGE_NAME_INPUT_LABEL = () => `Name`;
|
||||
export const SIGNUP_PAGE_PASSWORD_INPUT_LABEL = () => `Password`;
|
||||
export const SIGNUP_PAGE_PASSWORD_INPUT_PLACEHOLDER = () => `Password`;
|
||||
export const SIGNUP_PAGE_PASSWORD_INPUT_PLACEHOLDER = () =>
|
||||
`Enter your password`;
|
||||
export const SIGNUP_PAGE_LOGIN_LINK_TEXT = () => `Sign in`;
|
||||
export const SIGNUP_PAGE_NAME_INPUT_SUBTEXT = () => `How should we call you?`;
|
||||
export const SIGNUP_PAGE_SUBMIT_BUTTON_TEXT = () => `Sign up`;
|
||||
export const ALREADY_HAVE_AN_ACCOUNT = () => `Already have an account?`;
|
||||
export const LOOKING_TO_SELF_HOST = () => "Looking to self-host Appsmith?";
|
||||
export const VISIT_OUR_DOCS = () => "Visit our docs";
|
||||
|
||||
export const SIGNUP_PAGE_SUCCESS = () =>
|
||||
`Awesome! You have successfully registered.`;
|
||||
|
|
@ -110,11 +112,14 @@ export const RESET_PASSWORD_INVALID_TOKEN = () =>
|
|||
export const RESET_PASSWORD_FORGOT_PASSWORD_LINK = () => `Forgot password`;
|
||||
|
||||
export const FORGOT_PASSWORD_PAGE_EMAIL_INPUT_LABEL = () => `Email`;
|
||||
export const FORGOT_PASSWORD_PAGE_EMAIL_INPUT_PLACEHOLDER = () => `Email`;
|
||||
export const FORGOT_PASSWORD_PAGE_EMAIL_INPUT_PLACEHOLDER = () =>
|
||||
`Enter your email`;
|
||||
export const FORGOT_PASSWORD_PAGE_TITLE = () => `Reset password`;
|
||||
export const FORGOT_PASSWORD_PAGE_SUB_TITLE = () =>
|
||||
`Enter the email address associated with your account`;
|
||||
export const FORGOT_PASSWORD_PAGE_SUBTITLE = () =>
|
||||
`We will send a reset link to the email below`;
|
||||
export const FORGOT_PASSWORD_PAGE_SUBMIT_BUTTON_TEXT = () => `Reset`;
|
||||
export const FORGOT_PASSWORD_PAGE_SUBMIT_BUTTON_TEXT = () => `Send reset link`;
|
||||
export const FORGOT_PASSWORD_SUCCESS_TEXT = (email: string) =>
|
||||
`A password reset link has been sent to your email address ${email} registered with Appsmith.`;
|
||||
|
||||
|
|
@ -122,12 +127,12 @@ export const VERIFICATION_PENDING_TITLE = () => `Check your inbox`;
|
|||
export const VERIFICATION_PENDING_BODY = () =>
|
||||
`To finish your account setup click on the verification link we have sent in an email to `;
|
||||
|
||||
export const VERIFICATION_PENDING_NOT_YOU = () => `(not you?)`;
|
||||
export const VERIFICATION_PENDING_NOT_YOU = () => `Not you?`;
|
||||
|
||||
export const VERIFICATION_PENDING_NO_EMAIL = () =>
|
||||
`No email in your inbox or spam folder?`;
|
||||
|
||||
export const VERIFICATION_PENDING_RESEND_LINK = () => `Resend the link`;
|
||||
export const VERIFICATION_PENDING_RESEND_LINK = () => `Resend link`;
|
||||
|
||||
export const VERIFY_ERROR_ALREADY_VERIFIED_TITLE = () =>
|
||||
`Email already verified`;
|
||||
|
|
|
|||
|
|
@ -126,6 +126,8 @@ import AnalyticsUtil from "utils/AnalyticsUtil";
|
|||
import { useIsMobileDevice } from "utils/hooks/useDeviceDetect";
|
||||
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
|
||||
import CreateNewAppFromTemplatesWrapper from "./CreateNewAppFromTemplateModal/CreateNewAppFromTemplatesWrapper";
|
||||
import { getAssetUrl } from "@appsmith/utils/airgapHelpers";
|
||||
import { ASSETS_CDN_URL } from "constants/ThirdPartyConstants";
|
||||
|
||||
export const { cloudHosting } = getAppsmithConfigs();
|
||||
|
||||
|
|
@ -644,7 +646,7 @@ export function ApplicationsSection(props: any) {
|
|||
<div className="flex flex-col items-center justify-center mt-[180px]">
|
||||
<img
|
||||
className="mb-6"
|
||||
src="https://assets.appsmith.com/no-workspace-found.svg"
|
||||
src={`${getAssetUrl(`${ASSETS_CDN_URL}/no-workspace-found.svg`)}`}
|
||||
/>
|
||||
<NewText className="!mb-3 !font-semibold" kind="heading-s">
|
||||
{createMessage(NO_WORKSPACE_HEADING)}
|
||||
|
|
|
|||
|
|
@ -352,7 +352,8 @@ export type EventName =
|
|||
| "MULTI_FILE_PICKER_EXCEEDS_LIMIT"
|
||||
| "TEMPLATE_ADD_PAGE_FROM_TEMPLATE_FLOW"
|
||||
| HOMEPAGE_CREATE_APP_FROM_TEMPLATE_EVENTS
|
||||
| "EDITOR_MODE_CHANGE";
|
||||
| "EDITOR_MODE_CHANGE"
|
||||
| "VISIT_SELF_HOST_DOCS";
|
||||
|
||||
type HOMEPAGE_CREATE_APP_FROM_TEMPLATE_EVENTS =
|
||||
| "TEMPLATE_DROPDOWN_CLICK"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,12 @@ import { Form } from "redux-form";
|
|||
import styled from "styled-components";
|
||||
|
||||
const StyledForm = styled(Form)`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
.bp3-form-group {
|
||||
margin: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledForm;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ export const GITHUB_RELEASE_URL =
|
|||
"https://github.com/appsmithorg/appsmith/releases/tag";
|
||||
export const GET_RELEASE_NOTES_URL = (tagName: string) =>
|
||||
`${GITHUB_RELEASE_URL}/${tagName}`;
|
||||
export const SELF_HOSTING_DOC =
|
||||
"https://docs.appsmith.com/getting-started/setup";
|
||||
export const GOOGLE_MAPS_SETUP_DOC =
|
||||
"https://docs.appsmith.com/getting-started/setup/instance-configuration/google-maps";
|
||||
export const GOOGLE_SIGNUP_SETUP_DOC =
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
import React from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
import FooterLinks from "./FooterLinks";
|
||||
import { getTenantConfig } from "@appsmith/selectors/tenantSelectors";
|
||||
import { getAssetUrl } from "@appsmith/utils/airgapHelpers";
|
||||
import LeftSideContent from "./LeftSideContent";
|
||||
import { getAppsmithConfigs } from "@appsmith/configs";
|
||||
import { useIsMobileDevice } from "utils/hooks/useDeviceDetect";
|
||||
import styled from "styled-components";
|
||||
|
||||
interface ContainerProps {
|
||||
title: string;
|
||||
|
|
@ -14,38 +17,67 @@ interface ContainerProps {
|
|||
testId?: string;
|
||||
}
|
||||
|
||||
const ContainerWrapper = styled.div`
|
||||
a {
|
||||
span {
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const BoxWrapper = styled.div<{ isMobileView: boolean }>`
|
||||
box-shadow: 0px 1px 20px 0px rgba(76, 86, 100, 0.11);
|
||||
border-radius: var(--ads-v2-border-radius);
|
||||
background: var(--ads-v2-color-bg);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--ads-v2-spaces-5);
|
||||
padding: 32px 24px;
|
||||
|
||||
${({ isMobileView }) =>
|
||||
isMobileView ? "border: 1px solid var(--ads-v2-color-border);" : ""}
|
||||
`;
|
||||
|
||||
function Container(props: ContainerProps) {
|
||||
const { children, footer, subtitle, testId, title } = props;
|
||||
const tenantConfig = useSelector(getTenantConfig);
|
||||
const { cloudHosting } = getAppsmithConfigs();
|
||||
const isMobileDevice = useIsMobileDevice();
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex flex-col items-center gap-4 my-auto min-w-min"
|
||||
<ContainerWrapper
|
||||
className={`gap-14 my-auto flex items-center justify-center min-w-min`}
|
||||
data-testid={testId}
|
||||
>
|
||||
<div className="bg-white border border-t-4 border-[color:var(--ads-v2\-color-border)] border-t-[color:var(--ads-v2\-color-border-brand)] py-8 px-6 w-[min(400px,80%)] flex flex-col gap-6 t--login-container rounded-[var(--ads-v2\-border-radius)]">
|
||||
<img
|
||||
className="h-8 mx-auto"
|
||||
src={getAssetUrl(tenantConfig.brandLogoUrl)}
|
||||
/>
|
||||
<div className="flex flex-col gap-2 text-center">
|
||||
<h1 className="text-xl font-semibold text-center text-[color:var(--ads-v2\-color-fg-emphasis)]">
|
||||
{title}
|
||||
</h1>
|
||||
{subtitle && (
|
||||
<p className="text-base text-center text-[color:var(--ads-v2\-color-fg)]">
|
||||
{subtitle}
|
||||
</p>
|
||||
)}
|
||||
{cloudHosting && !isMobileDevice && <LeftSideContent />}
|
||||
<BoxWrapper
|
||||
className={`t--login-container ${
|
||||
isMobileDevice ? "w-full" : "w-[min(400px,80%)]"
|
||||
}`}
|
||||
isMobileView={isMobileDevice}
|
||||
>
|
||||
{!isMobileDevice && (
|
||||
<img
|
||||
className="h-8 mx-auto"
|
||||
src={getAssetUrl(tenantConfig.brandLogoUrl)}
|
||||
/>
|
||||
)}
|
||||
<div className={`flex flex-col gap-4`}>
|
||||
<div className="flex flex-col gap-2 text-center">
|
||||
<h1 className="text-lg font-semibold text-center text-[color:var(--ads-v2\-color-fg-emphasis)]">
|
||||
{title}
|
||||
</h1>
|
||||
{subtitle && (
|
||||
<p className="text-[14px] text-center text-[color:var(--ads-v2\-color-fg)]">
|
||||
{subtitle}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{children}
|
||||
{footer}
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
<div className="bg-white border w-[min(400px,80%)] rounded-[var(--ads-v2\-border-radius)] border-[color:var(--ads-v2\-color-border)]">
|
||||
{footer}
|
||||
<FooterLinks />
|
||||
</div>
|
||||
</div>
|
||||
</BoxWrapper>
|
||||
</ContainerWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,35 @@
|
|||
import React from "react";
|
||||
import { Link } from "design-system";
|
||||
import styled from "styled-components";
|
||||
|
||||
const FooterWrapper = styled.div`
|
||||
width: 85%;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
a {
|
||||
display: inline;
|
||||
span {
|
||||
display: inline;
|
||||
svg {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
function FooterLinks() {
|
||||
return (
|
||||
<div className="flex items-center justify-center gap-4 px-2 py-2">
|
||||
<FooterWrapper>
|
||||
By using Appsmith, you are agreeing to our
|
||||
<Link target="_blank" to="/privacy-policy.html">
|
||||
Privacy policy
|
||||
privacy policy
|
||||
</Link>
|
||||
and
|
||||
<Link target="_blank" to="/terms-and-conditions.html">
|
||||
Terms and conditions
|
||||
terms of service
|
||||
</Link>
|
||||
</div>
|
||||
.
|
||||
</FooterWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,14 +14,14 @@ import {
|
|||
FORM_VALIDATION_EMPTY_EMAIL,
|
||||
FORM_VALIDATION_INVALID_EMAIL,
|
||||
FORGOT_PASSWORD_SUCCESS_TEXT,
|
||||
FORGOT_PASSWORD_PAGE_LOGIN_LINK,
|
||||
createMessage,
|
||||
FORGOT_PASSWORD_PAGE_SUB_TITLE,
|
||||
} from "@appsmith/constants/messages";
|
||||
import { AUTH_LOGIN_URL } from "constants/routes";
|
||||
import { FORGOT_PASSWORD_FORM_NAME } from "@appsmith/constants/forms";
|
||||
import FormTextField from "components/utils/ReduxFormTextField";
|
||||
import { FormGroup } from "design-system-old";
|
||||
import { Button, Link, Callout } from "design-system";
|
||||
import { Button, Link, Callout, Icon } from "design-system";
|
||||
import { isEmail, isEmptyString } from "utils/formhelpers";
|
||||
import type { ForgotPasswordFormValues } from "./helpers";
|
||||
import { forgotPasswordSubmitHandler } from "./helpers";
|
||||
|
|
@ -58,18 +58,25 @@ export const ForgotPassword = (props: ForgotPasswordProps) => {
|
|||
}
|
||||
}, [props.emailValue]);
|
||||
|
||||
const footerSection = (
|
||||
<div className="px-2 flex items-center justify-center text-center text-[color:var(--ads-v2\-color-fg)] text-[14px]">
|
||||
<Icon name="arrow-left-line" size="md" />
|
||||
Back to
|
||||
<Link
|
||||
className="text-sm justify-center"
|
||||
kind="primary"
|
||||
target="_self"
|
||||
to={AUTH_LOGIN_URL}
|
||||
>
|
||||
Sign in
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Container
|
||||
subtitle={
|
||||
<Link
|
||||
className="text-sm justify-center"
|
||||
startIcon="arrow-left-line"
|
||||
target="_self"
|
||||
to={AUTH_LOGIN_URL}
|
||||
>
|
||||
{createMessage(FORGOT_PASSWORD_PAGE_LOGIN_LINK)}
|
||||
</Link>
|
||||
}
|
||||
footer={footerSection}
|
||||
subtitle={createMessage(FORGOT_PASSWORD_PAGE_SUB_TITLE)}
|
||||
title={createMessage(FORGOT_PASSWORD_PAGE_TITLE)}
|
||||
>
|
||||
<FormMessagesContainer>
|
||||
|
|
|
|||
119
app/client/src/pages/UserAuth/LeftSideContent.tsx
Normal file
119
app/client/src/pages/UserAuth/LeftSideContent.tsx
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
import { getAssetUrl } from "@appsmith/utils/airgapHelpers";
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { ASSETS_CDN_URL } from "constants/ThirdPartyConstants";
|
||||
import { Avatar } from "design-system";
|
||||
|
||||
const Wrapper = styled.div`
|
||||
width: 432px;
|
||||
|
||||
.left-description {
|
||||
padding-bottom: 24px;
|
||||
border-bottom: 1px solid var(--ads-v2-color-border);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--ads-spaces-4);
|
||||
}
|
||||
|
||||
.left-description-container {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
font-size: 16px;
|
||||
font-style: italic;
|
||||
color: var(--ads-v2-color-gray-800);
|
||||
}
|
||||
|
||||
.left-description-author {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--ads-spaces-3);
|
||||
}
|
||||
|
||||
.left-description-author > div {
|
||||
font-weight: 500;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.dot {
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.client-logo-container {
|
||||
padding-top: 24px;
|
||||
}
|
||||
|
||||
.client-heading {
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
line-height: 1.33;
|
||||
text-align: center;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.client-logo-container img {
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.client-logo-container .client-logo-section {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
margin-bottom: 24px;
|
||||
gap: var(--ads-spaces-3);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
`;
|
||||
|
||||
function LeftSideContent() {
|
||||
return (
|
||||
<Wrapper>
|
||||
<div className="left-description">
|
||||
<div className="left-description-container">
|
||||
"We’d been looking for a tool like Appsmith for years. With
|
||||
Appsmith we were able to build a UI on top of 12 different Snowflake
|
||||
control tables. Appsmith was easy for our developers to learn, and
|
||||
it’s easy to implement."
|
||||
</div>
|
||||
<div className="left-description-author">
|
||||
<Avatar
|
||||
image={`${getAssetUrl(`${ASSETS_CDN_URL}/thomas-zwick.png`)}`}
|
||||
label="Thomas Zwick"
|
||||
size="sm"
|
||||
/>
|
||||
<div>Thomas Zwick</div>
|
||||
<div className="dot">·</div>
|
||||
<div>Director, Omron</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="client-logo-container">
|
||||
<div className="client-heading">
|
||||
Used by more than 10,000 organisations across the globe
|
||||
</div>
|
||||
<div className="client-logo-section">
|
||||
<img
|
||||
alt="GSK logo"
|
||||
src={`${getAssetUrl(`${ASSETS_CDN_URL}/gsk-logo-grey.svg`)}`}
|
||||
/>
|
||||
<img
|
||||
alt="Omron logo"
|
||||
src={`${getAssetUrl(`${ASSETS_CDN_URL}/omron-logo.svg`)}`}
|
||||
/>
|
||||
<img
|
||||
alt="Dropbox logo"
|
||||
src={`${getAssetUrl(`${ASSETS_CDN_URL}/dropbox-text-logo.svg`)}`}
|
||||
/>
|
||||
<img
|
||||
alt="AWS logo"
|
||||
src={`${getAssetUrl(`${ASSETS_CDN_URL}/aws-logo-grey.svg`)}`}
|
||||
/>
|
||||
<img
|
||||
alt="Twilio logo"
|
||||
src={`${getAssetUrl(`${ASSETS_CDN_URL}/twilio-logo.svg`)}`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default LeftSideContent;
|
||||
|
|
@ -24,7 +24,6 @@ import {
|
|||
LOGIN_PAGE_INVALID_CREDS_FORGOT_PASSWORD_LINK,
|
||||
NEW_TO_APPSMITH,
|
||||
createMessage,
|
||||
LOGIN_PAGE_SUBTITLE,
|
||||
} from "@appsmith/constants/messages";
|
||||
import { FormGroup } from "design-system-old";
|
||||
import { Button, Link, Callout } from "design-system";
|
||||
|
|
@ -33,7 +32,11 @@ import ThirdPartyAuth from "pages/UserAuth/ThirdPartyAuth";
|
|||
import { isEmail, isEmptyString } from "utils/formhelpers";
|
||||
import type { LoginFormValues } from "pages/UserAuth/helpers";
|
||||
|
||||
import { SpacedSubmitForm, FormActions } from "pages/UserAuth/StyledComponents";
|
||||
import {
|
||||
SpacedSubmitForm,
|
||||
FormActions,
|
||||
EmailFormWrapper,
|
||||
} from "pages/UserAuth/StyledComponents";
|
||||
import AnalyticsUtil from "utils/AnalyticsUtil";
|
||||
import { LOGIN_SUBMIT_PATH } from "@appsmith/constants/ApiConstants";
|
||||
import PerformanceTracker, {
|
||||
|
|
@ -125,10 +128,10 @@ export function Login(props: LoginFormProps) {
|
|||
}
|
||||
|
||||
const footerSection = isFormLoginEnabled && (
|
||||
<div className="px-2 py-4 flex align-center justify-center text-base text-center text-[color:var(--ads-v2\-color-fg)] text-[14px]">
|
||||
{createMessage(NEW_TO_APPSMITH)}
|
||||
<div className="px-2 flex align-center justify-center text-center text-[color:var(--ads-v2\-color-fg)] text-[14px]">
|
||||
{createMessage(NEW_TO_APPSMITH)}
|
||||
<Link
|
||||
className="t--sign-up t--signup-link pl-[var(--ads-v2\-spaces-3)]"
|
||||
className="t--sign-up t--signup-link"
|
||||
kind="primary"
|
||||
target="_self"
|
||||
to={signupURL}
|
||||
|
|
@ -139,11 +142,7 @@ export function Login(props: LoginFormProps) {
|
|||
);
|
||||
|
||||
return (
|
||||
<Container
|
||||
footer={footerSection}
|
||||
subtitle={createMessage(LOGIN_PAGE_SUBTITLE)}
|
||||
title={createMessage(LOGIN_PAGE_TITLE)}
|
||||
>
|
||||
<Container footer={footerSection} title={createMessage(LOGIN_PAGE_TITLE)}>
|
||||
<Helmet>
|
||||
<title>{htmlPageTitle}</title>
|
||||
</Helmet>
|
||||
|
|
@ -171,7 +170,7 @@ export function Login(props: LoginFormProps) {
|
|||
<ThirdPartyAuth logins={socialLoginList} type={"SIGNIN"} />
|
||||
)}
|
||||
{isFormLoginEnabled && (
|
||||
<>
|
||||
<EmailFormWrapper>
|
||||
<SpacedSubmitForm action={loginURL} method="POST">
|
||||
<FormGroup
|
||||
intent={error ? "danger" : "none"}
|
||||
|
|
@ -218,12 +217,13 @@ export function Login(props: LoginFormProps) {
|
|||
</SpacedSubmitForm>
|
||||
<Link
|
||||
className="justify-center"
|
||||
kind="secondary"
|
||||
target="_self"
|
||||
to={forgotPasswordURL}
|
||||
>
|
||||
{createMessage(LOGIN_PAGE_FORGOT_PASSWORD_TEXT)}
|
||||
</Link>
|
||||
</>
|
||||
</EmailFormWrapper>
|
||||
)}
|
||||
</Container>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { RESET_PASSWORD_FORM_NAME } from "@appsmith/constants/forms";
|
|||
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
|
||||
import { getIsTokenValid, getIsValidatingToken } from "selectors/authSelectors";
|
||||
import FormTextField from "components/utils/ReduxFormTextField";
|
||||
import { Button, Callout, Link } from "design-system";
|
||||
import { Button, Callout, Icon, Link } from "design-system";
|
||||
import Spinner from "components/editorComponents/Spinner";
|
||||
import StyledForm from "components/editorComponents/Form";
|
||||
import { isEmptyString, isStrongPassword } from "utils/formhelpers";
|
||||
|
|
@ -20,7 +20,6 @@ import { AUTH_LOGIN_URL, FORGOT_PASSWORD_URL } from "constants/routes";
|
|||
import {
|
||||
RESET_PASSWORD_PAGE_PASSWORD_INPUT_LABEL,
|
||||
RESET_PASSWORD_PAGE_PASSWORD_INPUT_PLACEHOLDER,
|
||||
RESET_PASSWORD_LOGIN_LINK_TEXT,
|
||||
RESET_PASSWORD_SUBMIT_BUTTON_TEXT,
|
||||
RESET_PASSWORD_PAGE_TITLE,
|
||||
FORM_VALIDATION_INVALID_PASSWORD,
|
||||
|
|
@ -153,18 +152,25 @@ export function ResetPassword(props: ResetPasswordProps) {
|
|||
if (!isTokenValid && validatingToken) {
|
||||
return <Spinner />;
|
||||
}
|
||||
|
||||
const footerSection = (
|
||||
<div className="px-2 flex items-center justify-center text-center text-[color:var(--ads-v2\-color-fg)] text-[14px]">
|
||||
<Icon name="arrow-left-line" size="md" />
|
||||
Back to
|
||||
<Link
|
||||
className="text-sm justify-center"
|
||||
kind="primary"
|
||||
target="_self"
|
||||
to={AUTH_LOGIN_URL}
|
||||
>
|
||||
Sign in
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Container
|
||||
subtitle={
|
||||
<Link
|
||||
className="text-sm justify-center"
|
||||
startIcon="arrow-left-line"
|
||||
target="_self"
|
||||
to={AUTH_LOGIN_URL}
|
||||
>
|
||||
{createMessage(RESET_PASSWORD_LOGIN_LINK_TEXT)}
|
||||
</Link>
|
||||
}
|
||||
footer={footerSection}
|
||||
title={createMessage(RESET_PASSWORD_PAGE_TITLE)}
|
||||
>
|
||||
{(showSuccessMessage || showFailureMessage) && (
|
||||
|
|
|
|||
|
|
@ -5,7 +5,11 @@ import { AUTH_LOGIN_URL } from "constants/routes";
|
|||
import { SIGNUP_FORM_NAME } from "@appsmith/constants/forms";
|
||||
import type { RouteComponentProps } from "react-router-dom";
|
||||
import { useHistory, useLocation, withRouter } from "react-router-dom";
|
||||
import { SpacedSubmitForm, FormActions } from "pages/UserAuth/StyledComponents";
|
||||
import {
|
||||
SpacedSubmitForm,
|
||||
FormActions,
|
||||
OrWithLines,
|
||||
} from "pages/UserAuth/StyledComponents";
|
||||
import {
|
||||
SIGNUP_PAGE_TITLE,
|
||||
SIGNUP_PAGE_EMAIL_INPUT_LABEL,
|
||||
|
|
@ -19,8 +23,9 @@ import {
|
|||
SIGNUP_PAGE_SUBMIT_BUTTON_TEXT,
|
||||
ALREADY_HAVE_AN_ACCOUNT,
|
||||
createMessage,
|
||||
SIGNUP_PAGE_SUBTITLE,
|
||||
GOOGLE_RECAPTCHA_KEY_ERROR,
|
||||
LOOKING_TO_SELF_HOST,
|
||||
VISIT_OUR_DOCS,
|
||||
} from "@appsmith/constants/messages";
|
||||
import FormTextField from "components/utils/ReduxFormTextField";
|
||||
import ThirdPartyAuth from "pages/UserAuth/ThirdPartyAuth";
|
||||
|
|
@ -54,13 +59,14 @@ import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
|
|||
import { FEATURE_FLAG } from "@appsmith/entities/FeatureFlag";
|
||||
import { getHTMLPageTitle } from "@appsmith/utils/BusinessFeatures/brandingPageHelpers";
|
||||
import log from "loglevel";
|
||||
import { SELF_HOSTING_DOC } from "constants/ThirdPartyConstants";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
grecaptcha: any;
|
||||
}
|
||||
}
|
||||
const { googleRecaptchaSiteKey } = getAppsmithConfigs();
|
||||
const { cloudHosting, googleRecaptchaSiteKey } = getAppsmithConfigs();
|
||||
|
||||
const validate = (values: SignupFormValues) => {
|
||||
const errors: SignupFormValues = {};
|
||||
|
|
@ -171,25 +177,40 @@ export function SignUp(props: SignUpFormProps) {
|
|||
};
|
||||
|
||||
const footerSection = (
|
||||
<div className="px-2 py-4 flex align-center justify-center text-base text-center text-[color:var(--ads-v2\-color-fg)] text-[14px]">
|
||||
{createMessage(ALREADY_HAVE_AN_ACCOUNT)}
|
||||
<Link
|
||||
className="t--sign-up t--signup-link pl-[var(--ads-v2\-spaces-3)]"
|
||||
kind="primary"
|
||||
target="_self"
|
||||
to={AUTH_LOGIN_URL}
|
||||
>
|
||||
{createMessage(SIGNUP_PAGE_LOGIN_LINK_TEXT)}
|
||||
</Link>
|
||||
</div>
|
||||
<>
|
||||
<div className="px-2 flex align-center justify-center text-center text-[color:var(--ads-v2\-color-fg)] text-[14px]">
|
||||
{createMessage(ALREADY_HAVE_AN_ACCOUNT)}
|
||||
<Link
|
||||
className="t--sign-up t--signup-link"
|
||||
kind="primary"
|
||||
target="_self"
|
||||
to={AUTH_LOGIN_URL}
|
||||
>
|
||||
{createMessage(SIGNUP_PAGE_LOGIN_LINK_TEXT)}
|
||||
</Link>
|
||||
</div>
|
||||
{cloudHosting && (
|
||||
<>
|
||||
<OrWithLines>or</OrWithLines>
|
||||
<div className="px-2 text-center text-[color:var(--ads-v2\-color-fg)] text-[14px]">
|
||||
{createMessage(LOOKING_TO_SELF_HOST)}
|
||||
<Link
|
||||
className="t--visit-docs t--visit-docs-link pl-[var(--ads-v2\-spaces-3)] justify-center"
|
||||
kind="primary"
|
||||
onClick={() => AnalyticsUtil.logEvent("VISIT_SELF_HOST_DOCS")}
|
||||
target="_self"
|
||||
to={`${SELF_HOSTING_DOC}?utm_source=cloudSignup`}
|
||||
>
|
||||
{createMessage(VISIT_OUR_DOCS)}
|
||||
</Link>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<Container
|
||||
footer={footerSection}
|
||||
subtitle={createMessage(SIGNUP_PAGE_SUBTITLE)}
|
||||
title={createMessage(SIGNUP_PAGE_TITLE)}
|
||||
>
|
||||
<Container footer={footerSection} title={createMessage(SIGNUP_PAGE_TITLE)}>
|
||||
<Helmet>
|
||||
<title>{htmlPageTitle}</title>
|
||||
</Helmet>
|
||||
|
|
|
|||
|
|
@ -98,6 +98,9 @@ export const AuthCardBody = styled.div`
|
|||
export const SpacedForm = styled(Form)``;
|
||||
|
||||
export const SpacedSubmitForm = styled.form`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
&& .bp3-label {
|
||||
color: var(--ads-v2-color-fg);
|
||||
margin-bottom: var(--ads-v2-spaces-2);
|
||||
|
|
@ -108,6 +111,15 @@ export const SpacedSubmitForm = styled.form`
|
|||
&:only-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
.bp3-form-group {
|
||||
margin: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
export const EmailFormWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
`;
|
||||
|
||||
export const FormActions = styled.div`
|
||||
|
|
@ -117,7 +129,6 @@ export const FormActions = styled.div`
|
|||
}
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
margin-top: ${(props) => props.theme.spaces[5]}px;
|
||||
& > label {
|
||||
margin-right: ${(props) => props.theme.spaces[11]}px;
|
||||
}
|
||||
|
|
@ -158,3 +169,29 @@ export const StyledFormGroup = styled(FormGroup)`
|
|||
margin-bottom: var(--ads-v2-spaces-2);
|
||||
}
|
||||
`;
|
||||
|
||||
export const OrWithLines = styled.div`
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
background-color: var(--ads-v2-color-border);
|
||||
content: "";
|
||||
display: inline-block;
|
||||
height: 1px;
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
&::before {
|
||||
right: 0.5em;
|
||||
margin-left: -50%;
|
||||
}
|
||||
|
||||
&::after {
|
||||
left: 0.5em;
|
||||
margin-right: -50%;
|
||||
}
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -12,12 +12,24 @@ import { Button } from "design-system";
|
|||
|
||||
const ThirdPartyAuthWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--ads-v2-spaces-3);
|
||||
width: 100%;
|
||||
flex-wrap: wrap;
|
||||
`;
|
||||
|
||||
const StyledButton = styled(Button)`
|
||||
flex: 1 0 171px;
|
||||
`;
|
||||
|
||||
type SignInType = "SIGNIN" | "SIGNUP";
|
||||
|
||||
const startIcon: {
|
||||
[key: string]: string;
|
||||
} = {
|
||||
Google: "google-colored",
|
||||
Github: "github-fill",
|
||||
};
|
||||
|
||||
function SocialLoginButton(props: {
|
||||
logo: string;
|
||||
name: string;
|
||||
|
|
@ -33,7 +45,7 @@ function SocialLoginButton(props: {
|
|||
url += `?redirectUrl=${encodeURIComponent(redirectUrl)}`;
|
||||
}
|
||||
return (
|
||||
<Button
|
||||
<StyledButton
|
||||
href={url}
|
||||
kind="secondary"
|
||||
onClick={() => {
|
||||
|
|
@ -55,14 +67,14 @@ function SocialLoginButton(props: {
|
|||
size="md"
|
||||
startIcon={
|
||||
["Google", "Github"].includes(props.name)
|
||||
? props.name.toLowerCase() + `-fill`
|
||||
? startIcon[props.name]
|
||||
: "key-2-line"
|
||||
}
|
||||
>
|
||||
<div className="login-method" data-testid={`login-with-${props.name}`}>
|
||||
{props.label ?? `Continue with ${props.name}`}
|
||||
{props.label ?? `${props.name}`}
|
||||
</div>
|
||||
</Button>
|
||||
</StyledButton>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
import React, { useEffect } from "react";
|
||||
import Container from "./Container";
|
||||
import { Button, Callout, Link, Text } from "design-system";
|
||||
import { Button, Callout, Icon, Link, Text } from "design-system";
|
||||
import { AUTH_LOGIN_URL } from "constants/routes";
|
||||
import {
|
||||
createMessage,
|
||||
DEFAULT_ERROR_MESSAGE,
|
||||
FORGOT_PASSWORD_PAGE_LOGIN_LINK,
|
||||
PAGE_CLIENT_ERROR_DESCRIPTION,
|
||||
VERIFY_ERROR_ALREADY_VERIFIED_TITLE,
|
||||
VERIFY_ERROR_EXPIRED_TITLE,
|
||||
|
|
@ -97,14 +96,16 @@ const VerificationError = (
|
|||
return (
|
||||
<Container
|
||||
footer={
|
||||
<div className="px-2 py-4 flex align-center justify-center text-base text-center text-[color:var(--ads-v2\-color-fg)] text-[14px]">
|
||||
<div className="px-2 py-4 flex items-center justify-center text-base text-center text-[color:var(--ads-v2\-color-fg)] text-[14px]">
|
||||
<Icon name="arrow-left-line" size="md" />
|
||||
Back to
|
||||
<Link
|
||||
className="pl-[var(--ads-v2\-spaces-3)]"
|
||||
className="text-sm justify-center pl-[var(--ads-v2\-spaces-3)]"
|
||||
kind="primary"
|
||||
target="_self"
|
||||
to={AUTH_LOGIN_URL}
|
||||
>
|
||||
{createMessage(FORGOT_PASSWORD_PAGE_LOGIN_LINK)}
|
||||
Sign in
|
||||
</Link>
|
||||
</div>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import React from "react";
|
|||
import Container from "./Container";
|
||||
import {
|
||||
createMessage,
|
||||
VERIFICATION_PENDING_BODY,
|
||||
VERIFICATION_PENDING_NO_EMAIL,
|
||||
VERIFICATION_PENDING_NOT_YOU,
|
||||
VERIFICATION_PENDING_RESEND_LINK,
|
||||
|
|
@ -33,16 +32,21 @@ const VerificationPending = (props: RouteComponentProps<{ email: string }>) => {
|
|||
|
||||
return (
|
||||
<Container
|
||||
footer={
|
||||
<div className="px-2 flex align-center justify-center text-center text-[color:var(--ads-v2\-color-fg)] text-[14px]">
|
||||
<Link kind="primary" target="_self" to={AUTH_LOGIN_URL}>
|
||||
{createMessage(VERIFICATION_PENDING_NOT_YOU)}
|
||||
</Link>
|
||||
</div>
|
||||
}
|
||||
testId="verification-pending"
|
||||
title={createMessage(VERIFICATION_PENDING_TITLE)}
|
||||
>
|
||||
<Body>
|
||||
<Text kind={"body-m"}>
|
||||
{createMessage(VERIFICATION_PENDING_BODY)} <Email>{email}</Email>
|
||||
Click the verification link sent to <Email>{email}</Email> to finish
|
||||
setting up your account.
|
||||
</Text>
|
||||
<Link kind="primary" target="_self" to={AUTH_LOGIN_URL}>
|
||||
{createMessage(VERIFICATION_PENDING_NOT_YOU)}
|
||||
</Link>
|
||||
</Body>
|
||||
<Body>
|
||||
<Text kind="body-m">
|
||||
|
|
|
|||
|
|
@ -14,6 +14,11 @@ import { ThemeProvider } from "styled-components";
|
|||
import VerificationPending from "./VerificationPending";
|
||||
import VerifyUser from "./VerifyUser";
|
||||
import VerificationError from "./VerificationError";
|
||||
import FooterLinks from "./FooterLinks";
|
||||
import { useIsMobileDevice } from "utils/hooks/useDeviceDetect";
|
||||
import { getAssetUrl } from "@appsmith/utils/airgapHelpers";
|
||||
import { getTenantConfig } from "@appsmith/selectors/tenantSelectors";
|
||||
import { getAppsmithConfigs } from "@appsmith/configs";
|
||||
|
||||
const SentryRoute = Sentry.withSentryRouting(Route);
|
||||
|
||||
|
|
@ -23,11 +28,24 @@ export function UserAuth() {
|
|||
const lightTheme = useSelector((state: AppState) =>
|
||||
getThemeDetails(state, ThemeMode.LIGHT),
|
||||
);
|
||||
const isMobileDevice = useIsMobileDevice();
|
||||
const tenantConfig = useSelector(getTenantConfig);
|
||||
const { cloudHosting } = getAppsmithConfigs();
|
||||
|
||||
return (
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
{/* TODO: (Albin) - chnages this to ads-v2 variable once branding is sorted out. */}
|
||||
<div className="absolute inset-0 flex flex-col overflow-y-auto auth-container bg-[color:var(--ads-color-background-secondary)] p-4 t--auth-container">
|
||||
<div
|
||||
className={`absolute inset-0 flex flex-col overflow-y-auto auth-container bg-[color:var(--ads-color-background-secondary)] ${
|
||||
!isMobileDevice ? "p-4" : "px-6 py-12"
|
||||
} t--auth-container justify-between`}
|
||||
>
|
||||
{isMobileDevice && (
|
||||
<img
|
||||
className="h-8 mx-auto"
|
||||
src={getAssetUrl(tenantConfig.brandLogoUrl)}
|
||||
/>
|
||||
)}
|
||||
<Switch location={location}>
|
||||
<SentryRoute component={Login} exact path={`${path}/login`} />
|
||||
<SentryRoute component={SignUp} exact path={`${path}/signup`} />
|
||||
|
|
@ -54,6 +72,7 @@ export function UserAuth() {
|
|||
/>
|
||||
<SentryRoute component={PageNotFound} />
|
||||
</Switch>
|
||||
{cloudHosting && <FooterLinks />}
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -13257,7 +13257,7 @@ __metadata:
|
|||
d3-geo: ^3.1.0
|
||||
dayjs: ^1.10.6
|
||||
deep-diff: ^1.0.2
|
||||
design-system: "npm:@appsmithorg/design-system@2.1.36"
|
||||
design-system: "npm:@appsmithorg/design-system@2.1.37"
|
||||
design-system-old: "npm:@appsmithorg/design-system-old@1.1.16"
|
||||
diff: ^5.0.0
|
||||
dotenv: ^8.1.0
|
||||
|
|
@ -17288,9 +17288,9 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"design-system@npm:@appsmithorg/design-system@2.1.36":
|
||||
version: 2.1.36
|
||||
resolution: "@appsmithorg/design-system@npm:2.1.36"
|
||||
"design-system@npm:@appsmithorg/design-system@2.1.37":
|
||||
version: 2.1.37
|
||||
resolution: "@appsmithorg/design-system@npm:2.1.37"
|
||||
dependencies:
|
||||
"@radix-ui/react-dialog": ^1.0.2
|
||||
"@radix-ui/react-dropdown-menu": ^2.0.4
|
||||
|
|
@ -17320,7 +17320,7 @@ __metadata:
|
|||
react-dom: ^17.0.2
|
||||
react-router-dom: ^5.0.0
|
||||
styled-components: ^5.3.6
|
||||
checksum: 410db12c576560c6195d5b9ed776f1172c00985966b39d75fbfff119694f2227c94521e5e6d1c3068dd2d49fbac37975c2bc05b1284a09ef434ac7bd551bf84c
|
||||
checksum: 012936cc603bf5c21bcd6486c35935bc22171f2243dd988cb93f92ed559fcd3245561ceda2f791845dce559de942c2acce8cde1fc8c3d30d45c24d4768aedf72
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user