Merge pull request #1184 from ONLYOFFICE/feature/login-error-page
Feature/login error page
This commit is contained in:
commit
ecd1e8537e
@ -3,27 +3,27 @@ import StyledButton from "@docspace/components/button/styled-button";
|
||||
import Base from "@docspace/components/themes/base";
|
||||
|
||||
const activeCss = css`
|
||||
border-color: ${(props) => props.$currentColorScheme.main.buttons};
|
||||
border-color: ${(props) => props.$currentColorScheme.main?.buttons};
|
||||
|
||||
background: ${(props) =>
|
||||
props.primary && props.$currentColorScheme.main.buttons};
|
||||
props.primary && props.$currentColorScheme.main?.buttons};
|
||||
|
||||
opacity: ${(props) => !props.isDisabled && "1"};
|
||||
|
||||
filter: ${(props) =>
|
||||
props.primary &&
|
||||
(props.theme.isBase ? "brightness(90%)" : "brightness(82%)")};
|
||||
color: ${(props) => props.$currentColorScheme.text.buttons};
|
||||
color: ${(props) => props.$currentColorScheme.text?.buttons};
|
||||
`;
|
||||
|
||||
const hoverCss = css`
|
||||
border-color: ${(props) => props.$currentColorScheme.main.buttons};
|
||||
border-color: ${(props) => props.$currentColorScheme.main?.buttons};
|
||||
|
||||
background: ${(props) =>
|
||||
props.primary && props.$currentColorScheme.main.buttons};
|
||||
props.primary && props.$currentColorScheme.main?.buttons};
|
||||
|
||||
opacity: ${(props) => props.primary && !props.isDisabled && "0.85"};
|
||||
color: ${(props) => props.primary && props.$currentColorScheme.text.buttons};
|
||||
color: ${(props) => props.primary && props.$currentColorScheme.text?.buttons};
|
||||
`;
|
||||
|
||||
const getDefaultStyles = ({
|
||||
@ -41,16 +41,16 @@ const getDefaultStyles = ({
|
||||
css`
|
||||
${primary &&
|
||||
css`
|
||||
background: ${$currentColorScheme.main.buttons};
|
||||
background: ${$currentColorScheme.main?.buttons};
|
||||
opacity: ${isDisabled && "0.6"};
|
||||
border: ${`1px solid`} ${$currentColorScheme.main.buttons};
|
||||
color: ${$currentColorScheme.text.buttons};
|
||||
border: ${`1px solid`} ${$currentColorScheme.main?.buttons};
|
||||
color: ${$currentColorScheme.text?.buttons};
|
||||
|
||||
.loader {
|
||||
svg {
|
||||
color: ${$currentColorScheme.text.buttons};
|
||||
color: ${$currentColorScheme.text?.buttons};
|
||||
}
|
||||
background-color: ${$currentColorScheme.main.buttons};
|
||||
background-color: ${$currentColorScheme.main?.buttons};
|
||||
}
|
||||
`}
|
||||
|
||||
|
@ -5,7 +5,7 @@ const getDefaultStyles = ({ $currentColorScheme }) =>
|
||||
$currentColorScheme &&
|
||||
css`
|
||||
.login-link {
|
||||
color: ${$currentColorScheme.main.accent};
|
||||
color: ${$currentColorScheme?.main?.accent};
|
||||
}
|
||||
`;
|
||||
|
||||
|
@ -7,8 +7,8 @@ const getDefaultStyles = ({ $currentColorScheme, hasError, theme }) =>
|
||||
css`
|
||||
:focus-within {
|
||||
border-color: ${hasError
|
||||
? theme.textArea.focusErrorBorderColor
|
||||
: $currentColorScheme.main.accent};
|
||||
? theme?.textArea.focusErrorBorderColor
|
||||
: $currentColorScheme.main?.accent};
|
||||
}
|
||||
`;
|
||||
|
||||
|
@ -334,13 +334,22 @@ const ErrorContainer = (props) => {
|
||||
</svg>
|
||||
</div>
|
||||
{headerText && (
|
||||
<Headline id="header" type="header">
|
||||
<Headline id="header" type="header" theme={rest?.theme}>
|
||||
{headerText}
|
||||
</Headline>
|
||||
)}
|
||||
{bodyText && <Text id="text">{bodyText}</Text>}
|
||||
{bodyText && (
|
||||
<Text id="text" theme={rest?.theme}>
|
||||
{bodyText}
|
||||
</Text>
|
||||
)}
|
||||
{customizedBodyText && (
|
||||
<Text id="customized-text" fontWeight={600} fontSize="13px">
|
||||
<Text
|
||||
id="customized-text"
|
||||
fontWeight={600}
|
||||
theme={rest?.theme}
|
||||
fontSize="13px"
|
||||
>
|
||||
{customizedBodyText}
|
||||
</Text>
|
||||
)}
|
||||
@ -348,6 +357,7 @@ const ErrorContainer = (props) => {
|
||||
{buttonText && buttonUrl && (
|
||||
<div id="button-container">
|
||||
<Button
|
||||
theme={rest?.theme}
|
||||
id="button"
|
||||
size="normal"
|
||||
scale
|
||||
|
@ -95,7 +95,7 @@ const MoreLoginModal: React.FC<IMoreLoginNodalProps> = (props) => {
|
||||
/>
|
||||
</ProviderRow>
|
||||
)}
|
||||
{providers.map((item, index) => {
|
||||
{providers?.map((item, index) => {
|
||||
if (!providersData[item.provider]) return;
|
||||
|
||||
const { icon, label } = providersData[item.provider];
|
||||
|
@ -375,3 +375,5 @@ export const PortalFeaturesLimitations = Object.freeze({
|
||||
});
|
||||
|
||||
export const EDITOR_ID = "docspace_editor";
|
||||
|
||||
export const wrongPortalNameUrl = `https://www.onlyoffice.com/wrongportalname.aspx`;
|
||||
|
@ -12,6 +12,7 @@ import { version } from "../package.json";
|
||||
import SocketIOHelper from "../utils/socket";
|
||||
import { Dark, Base } from "@docspace/components/themes";
|
||||
import { initPluginStore } from "../../client/src/helpers/plugins";
|
||||
import { wrongPortalNameUrl } from "@docspace/common/constants";
|
||||
|
||||
const themes = {
|
||||
Dark: Dark,
|
||||
@ -241,7 +242,7 @@ class SettingsStore {
|
||||
if (err?.response?.status === 404) {
|
||||
// portal not found
|
||||
return window.location.replace(
|
||||
`https://www.onlyoffice.com/wrongportalname.aspx?url=${window.location.hostname}`
|
||||
`${wrongPortalNameUrl}?url=${window.location.hostname}`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -466,7 +466,7 @@ export const getConvertedSize = (t, size) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const getBgPattern = (colorSchemeId: number) => {
|
||||
export const getBgPattern = (colorSchemeId: number | undefined) => {
|
||||
switch (colorSchemeId) {
|
||||
case 1:
|
||||
return `url('${BackgroundPatternReactSvgUrl}')`;
|
||||
|
25
packages/login/index.d.ts
vendored
25
packages/login/index.d.ts
vendored
@ -56,6 +56,7 @@ declare global {
|
||||
utcHoursOffset: number;
|
||||
utcOffset: string;
|
||||
version: string;
|
||||
standalone: boolean;
|
||||
}
|
||||
|
||||
interface IBuildInfo {
|
||||
@ -89,15 +90,23 @@ declare global {
|
||||
selected: number;
|
||||
themes: ITheme[];
|
||||
}
|
||||
|
||||
interface IError {
|
||||
status: number;
|
||||
standalone: boolean;
|
||||
message: string | undefined;
|
||||
}
|
||||
|
||||
interface IInitialState {
|
||||
portalSettings: IPortalSettings;
|
||||
buildInfo: IBuildInfo;
|
||||
providers: ProvidersType;
|
||||
capabilities: ICapabilities;
|
||||
match: MatchType;
|
||||
currentColorScheme: ITheme;
|
||||
isAuth: boolean;
|
||||
logoUrls: any;
|
||||
portalSettings?: IPortalSettings;
|
||||
buildInfo?: IBuildInfo;
|
||||
providers?: ProvidersType;
|
||||
capabilities?: ICapabilities;
|
||||
match?: MatchType;
|
||||
currentColorScheme?: ITheme;
|
||||
isAuth?: boolean;
|
||||
logoUrls?: any;
|
||||
error?: IError;
|
||||
}
|
||||
|
||||
interface DevRequest {
|
||||
|
@ -6,12 +6,28 @@ import CodeLogin from "./components/CodeLogin";
|
||||
import initLoginStore from "../store";
|
||||
import { Provider as MobxProvider } from "mobx-react";
|
||||
import SimpleNav from "../client/components/sub-components/SimpleNav";
|
||||
import { wrongPortalNameUrl } from "@docspace/common/constants";
|
||||
|
||||
interface ILoginProps extends IInitialState {
|
||||
isDesktopEditor?: boolean;
|
||||
}
|
||||
const App: React.FC<ILoginProps> = (props) => {
|
||||
const loginStore = initLoginStore(props.currentColorScheme);
|
||||
const loginStore = initLoginStore(props?.currentColorScheme || {});
|
||||
|
||||
React.useEffect(() => {
|
||||
if (window && props.error) {
|
||||
const { status, standalone, message } = props.error;
|
||||
|
||||
if (status === 404 && !standalone) {
|
||||
window.location.replace(
|
||||
`${wrongPortalNameUrl}?url=${window.location.hostname}`
|
||||
);
|
||||
}
|
||||
|
||||
throw new Error(message);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<MobxProvider {...loginStore}>
|
||||
<SimpleNav {...props} />
|
||||
|
@ -1,15 +1,18 @@
|
||||
import React from "react";
|
||||
import ErrorContainer from "@docspace/common/components/ErrorContainer";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Dark, Base } from "@docspace/components/themes";
|
||||
|
||||
interface IError520Props {
|
||||
match?: {
|
||||
params: MatchType;
|
||||
};
|
||||
theme?: any;
|
||||
}
|
||||
|
||||
interface IErrorBoundaryProps extends IError520Props {
|
||||
onError?: (error: any, errorInfo: any) => void;
|
||||
theme?: any;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
@ -21,8 +24,21 @@ const Error520: React.FC<IError520Props> = ({ match }) => {
|
||||
const { t } = useTranslation(["Common"]);
|
||||
const { error } = (match && match.params) || {};
|
||||
|
||||
const theme =
|
||||
typeof window !== "undefined" &&
|
||||
window.matchMedia &&
|
||||
window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
? Dark
|
||||
: Base;
|
||||
|
||||
const themeProps = theme ? { theme } : {};
|
||||
|
||||
return (
|
||||
<ErrorContainer headerText={t("SomethingWentWrong")} bodyText={error} />
|
||||
<ErrorContainer
|
||||
headerText={t("SomethingWentWrong")}
|
||||
bodyText={error}
|
||||
{...themeProps}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -43,7 +43,7 @@ const Login: React.FC<ILoginProps> = ({
|
||||
logoUrls,
|
||||
}) => {
|
||||
const isRestoringPortal =
|
||||
portalSettings.tenantStatus === TenantStatus.PortalRestore;
|
||||
portalSettings?.tenantStatus === TenantStatus.PortalRestore;
|
||||
|
||||
useEffect(() => {
|
||||
isRestoringPortal && window.location.replace("/preparation-portal");
|
||||
@ -52,7 +52,11 @@ const Login: React.FC<ILoginProps> = ({
|
||||
const [moreAuthVisible, setMoreAuthVisible] = useState(false);
|
||||
const [recoverDialogVisible, setRecoverDialogVisible] = useState(false);
|
||||
|
||||
const { enabledJoin, greetingSettings, enableAdmMess } = portalSettings;
|
||||
const { enabledJoin, greetingSettings, enableAdmMess } = portalSettings || {
|
||||
enabledJoin: false,
|
||||
greetingSettings: false,
|
||||
enableAdmMess: false,
|
||||
};
|
||||
|
||||
const ssoLabel = capabilities?.ssoLabel;
|
||||
const ssoUrl = capabilities?.ssoUrl;
|
||||
@ -96,7 +100,7 @@ const Login: React.FC<ILoginProps> = ({
|
||||
const oauthDataExists = () => {
|
||||
let existProviders = 0;
|
||||
providers && providers.length > 0;
|
||||
providers.map((item) => {
|
||||
providers?.map((item) => {
|
||||
if (!providersData[item.provider]) return;
|
||||
existProviders++;
|
||||
});
|
||||
@ -187,10 +191,12 @@ const Login: React.FC<ILoginProps> = ({
|
||||
setRecoverDialogVisible(!recoverDialogVisible);
|
||||
};
|
||||
|
||||
const bgPattern = getBgPattern(currentColorScheme.id);
|
||||
const bgPattern = getBgPattern(currentColorScheme?.id);
|
||||
|
||||
const logo = Object.values(logoUrls)[1];
|
||||
const logoUrl = !theme.isBase
|
||||
const logo = logoUrls && Object.values(logoUrls)[1];
|
||||
const logoUrl = !logo
|
||||
? undefined
|
||||
: !theme?.isBase
|
||||
? getLogoFromPath(logo.path.dark)
|
||||
: getLogoFromPath(logo.path.light);
|
||||
|
||||
@ -242,7 +248,7 @@ const Login: React.FC<ILoginProps> = ({
|
||||
<LoginForm
|
||||
isDesktop={!!isDesktopEditor}
|
||||
isLoading={isLoading}
|
||||
hashSettings={portalSettings.passwordHash}
|
||||
hashSettings={portalSettings?.passwordHash}
|
||||
setIsLoading={setIsLoading}
|
||||
onRecoverDialogVisible={onRecoverDialogVisible}
|
||||
match={match}
|
||||
|
@ -59,7 +59,10 @@ const LoginForm: React.FC<ILoginFormProps> = ({
|
||||
|
||||
const { t } = useTranslation(["Login", "Common"]);
|
||||
|
||||
const { message, confirmedEmail } = match;
|
||||
const { message, confirmedEmail } = match || {
|
||||
message: "",
|
||||
confirmedEmail: "",
|
||||
};
|
||||
|
||||
const authCallback = (profile: string) => {
|
||||
localStorage.removeItem("profile");
|
||||
|
@ -10,11 +10,11 @@ const StyledNav = styled.div`
|
||||
height: 48px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: ${(props) => props.theme.login.navBackground};
|
||||
background-color: ${(props) => props.theme?.login?.navBackground};
|
||||
|
||||
svg {
|
||||
path:last-child {
|
||||
fill: ${(props) => props.theme.client.home.logoColor};
|
||||
fill: ${(props) => props.theme.client?.home?.logoColor};
|
||||
}
|
||||
}
|
||||
@media ${hugeMobile} {
|
||||
@ -23,9 +23,11 @@ const StyledNav = styled.div`
|
||||
`;
|
||||
|
||||
const SimpleNav = ({ theme, logoUrls }) => {
|
||||
const logo = Object.values(logoUrls)[0];
|
||||
const logo = logoUrls && Object.values(logoUrls)[0];
|
||||
|
||||
const logoUrl = !theme.isBase
|
||||
const logoUrl = !logo
|
||||
? undefined
|
||||
: !theme?.isBase
|
||||
? getLogoFromPath(logo.path.dark)
|
||||
: getLogoFromPath(logo.path.light);
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { getAppearanceTheme } from "@docspace/common/api/settings";
|
||||
import express, { Response } from "express";
|
||||
import template from "./lib/template";
|
||||
import path from "path";
|
||||
@ -44,8 +45,9 @@ app.use(logger("dev", { stream: stream }));
|
||||
|
||||
app.get("*", async (req: ILoginRequest, res: Response, next) => {
|
||||
const { i18n, cookies, headers, query, t, url } = req;
|
||||
let initialState: IInitialState;
|
||||
let initialState: IInitialState = {};
|
||||
let assets: assetsType;
|
||||
let standalone = false;
|
||||
|
||||
initSSR(headers);
|
||||
|
||||
@ -57,7 +59,8 @@ app.get("*", async (req: ILoginRequest, res: Response, next) => {
|
||||
next();
|
||||
}
|
||||
|
||||
let currentLanguage: string = initialState.portalSettings.culture;
|
||||
let currentLanguage: string = initialState?.portalSettings?.culture || "en";
|
||||
standalone = initialState?.portalSettings?.standalone ? true : false;
|
||||
|
||||
if (cookies && cookies[LANGUAGE]) {
|
||||
currentLanguage = cookies[LANGUAGE];
|
||||
@ -103,7 +106,42 @@ app.get("*", async (req: ILoginRequest, res: Response, next) => {
|
||||
if (e instanceof Error) {
|
||||
message = e.message;
|
||||
}
|
||||
|
||||
const status = e?.response?.status === 404 ? 404 : 520;
|
||||
|
||||
if (status !== 404 && !initialState.currentColorScheme) {
|
||||
const availableThemes: IThemes = await getAppearanceTheme();
|
||||
|
||||
const currentColorScheme = availableThemes.themes.find((theme) => {
|
||||
return availableThemes.selected === theme.id;
|
||||
});
|
||||
|
||||
initialState.currentColorScheme = currentColorScheme;
|
||||
}
|
||||
|
||||
initialState.error = {
|
||||
status,
|
||||
standalone,
|
||||
message,
|
||||
};
|
||||
|
||||
const { component, styleTags } = renderApp(i18n, initialState, url);
|
||||
|
||||
assets = await getAssets();
|
||||
|
||||
const htmlString = template(
|
||||
initialState,
|
||||
component,
|
||||
styleTags,
|
||||
{},
|
||||
"en",
|
||||
assets,
|
||||
t
|
||||
);
|
||||
|
||||
winston.error(message);
|
||||
|
||||
res.send(htmlString);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -35,7 +35,9 @@ const template: Template = (
|
||||
? `${t("Authorization")} – ${organizationName}`
|
||||
: title;
|
||||
|
||||
const favicon = getLogoFromPath(initLoginState.logoUrls[2]?.path?.light);
|
||||
const favicon = getLogoFromPath(
|
||||
initLoginState?.logoUrls && initLoginState?.logoUrls[2]?.path?.light
|
||||
);
|
||||
|
||||
let clientScripts =
|
||||
assets && assets.hasOwnProperty("client.js")
|
||||
|
@ -15,7 +15,7 @@ class LoginStore {
|
||||
|
||||
setTheme = (theme) => {
|
||||
this.theme = theme;
|
||||
}
|
||||
};
|
||||
|
||||
setCurrentColorScheme = (currentColorScheme: ITheme) => {
|
||||
this.currentColorScheme = currentColorScheme;
|
||||
|
Loading…
Reference in New Issue
Block a user