Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 57 additions & 25 deletions apps/console/src/protected-app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
useAuthContext
} from "@asgardeo/auth-react";
import useSignIn from "@wso2is/admin.authentication.v1/hooks/use-sign-in";
import LogoImage from "@wso2is/admin.core.v1/components/logo-image";
import { PreLoader } from "@wso2is/admin.core.v1/components/pre-loader";
import { Config } from "@wso2is/admin.core.v1/configs/app";
import { AppConstants } from "@wso2is/admin.core.v1/constants/app-constants";
Expand Down Expand Up @@ -114,6 +115,8 @@ export const ProtectedApp: FunctionComponent<AppPropsInterface> = (): ReactEleme
const [ routesFiltered, setRoutesFiltered ] = useState<boolean>(false);
const [ isUserTenantless, setIsUserTenantless ] = useState(undefined);

const [ switchingOrganization, setSwitchingOrganization ] = useState(null);

useEffect(() => {
dispatch(
setDeploymentConfigs<DeploymentConfigInterface>(
Expand Down Expand Up @@ -353,33 +356,62 @@ export const ProtectedApp: FunctionComponent<AppPropsInterface> = (): ReactEleme
filterRoutes(() => setRoutesFiltered(true), isUserTenantless, isFirstLevelOrg);
}, [ filterRoutes, state.isAuthenticated, isFirstLevelOrg, isUserTenantless ]);

return (
<SecureApp
fallback={ <PreLoader /> }
overrideSignIn={ async () => {
const prompt: string = new URL(location.href).searchParams.get("prompt");
const fidp: string = new URL(location.href).searchParams.get("fidp");

// This is to prompt the SSO page if a user tries to sign in
// through a federated IdP using an existing email address.
if (prompt) {
await signIn({ prompt: "login" });
} else {
const authParams: { fidp?: string; } = {};
useEffect(() => {
document.addEventListener("orgSwitchStart", (event: CustomEvent) => {
setSwitchingOrganization(event?.detail?.organization);
});

if (fidp) {
authParams["fidp"] = fidp;
document.addEventListener("orgSwitchEnd", (_event: CustomEvent) => {
setSwitchingOrganization(null);
});
}, []);

return (
<>
{ switchingOrganization && (<div
className="ui modal transition"
style={ {
alignContent: "center",
alignItems: "center",
borderRadius: 0,
display: "flex",
flexDirection: "column",
height: "100vh",
justifyContent: "center",
width: "100vw"
} }>
<LogoImage />
<p className="mt-5">Switching to <strong>{ switchingOrganization?.name }</strong> organization</p>
</div>) }
<SecureApp
fallback={ <PreLoader /> }
overrideSignIn={ async () => {
const prompt: string = new URL(location.href).searchParams.get("prompt");
const fidp: string = new URL(location.href).searchParams.get("fidp");

// This is to prompt the SSO page if a user tries to sign in
// through a federated IdP using an existing email address.
if (prompt) {
await signIn({ prompt: "login" });
} else {
const authParams: { fidp?: string; } = {};

if (fidp) {
authParams["fidp"] = fidp;
}

await signIn(authParams);
}
} }
>
<I18nextProvider i18n={ I18n.instance }>
<SubscriptionProvider tierName={ tenantTier?.tierName ?? TenantTier.FREE }>
{ renderApp && routesFiltered ? <App /> : <PreLoader /> }
</SubscriptionProvider>
</I18nextProvider>
</SecureApp>

</>

await signIn(authParams);
}
} }
>
<I18nextProvider i18n={ I18n.instance }>
<SubscriptionProvider tierName={ tenantTier?.tierName ?? TenantTier.FREE }>
{ renderApp && routesFiltered ? <App /> : <PreLoader /> }
</SubscriptionProvider>
</I18nextProvider>
</SecureApp>
);
};
27 changes: 3 additions & 24 deletions features/admin.core.v1/components/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import Chip from "@oxygen-ui/react/Chip";
import Divider from "@oxygen-ui/react/Divider";
import OxygenHeader, { HeaderProps } from "@oxygen-ui/react/Header";
import Image from "@oxygen-ui/react/Image";
import Link from "@oxygen-ui/react/Link";
import ListItemIcon from "@oxygen-ui/react/ListItemIcon";
import ListItemText from "@oxygen-ui/react/ListItemText";
Expand All @@ -39,10 +38,8 @@
import { useGetCurrentOrganizationType } from "@wso2is/admin.organizations.v1/hooks/use-get-organization-type";
import useSubscription, { UseSubscriptionInterface } from "@wso2is/admin.subscription.v1/hooks/use-subscription";
import { TenantTier } from "@wso2is/admin.subscription.v1/models/tenant-tier";
import { resolveAppLogoFilePath } from "@wso2is/core/helpers";
import { IdentifiableComponentInterface, ProfileInfoInterface } from "@wso2is/core/models";
import { FeatureAccessConfigInterface } from "@wso2is/core/src/models";
import { StringUtils } from "@wso2is/core/utils";
import { I18n } from "@wso2is/i18n";
import { useDocumentation } from "@wso2is/react-components";
import React, { FunctionComponent, ReactElement, ReactNode, useEffect, useMemo, useState } from "react";
Expand All @@ -64,6 +61,7 @@
import { CommonUtils } from "../utils/common-utils";
import { EventPublisher } from "../utils/event-publisher";
import "./header.scss";
import LogoImage from "./logo-image";

Check warning on line 64 in features/admin.core.v1/components/header.tsx

View workflow job for this annotation

GitHub Actions / ⬣ ESLint (STATIC ANALYSIS) (lts/*, 8.7.4)

`./logo-image` import should occur before import of `./modals/feature-preview-modal`

/**
* Dashboard layout Prop types.
Expand Down Expand Up @@ -353,33 +351,14 @@
return accountAppURL;
};

const LOGO_IMAGE = () => {
return (
<Image
src={ resolveAppLogoFilePath(
window["AppUtils"].getConfig().ui.appLogo?.defaultLogoPath ??
window["AppUtils"].getConfig().ui.appLogoPath,
`${window["AppUtils"].getConfig().clientOrigin}/` +
`${
StringUtils.removeSlashesFromPath(window["AppUtils"].getConfig().appBase) !== ""
? StringUtils.removeSlashesFromPath(window["AppUtils"].getConfig().appBase) + "/"
: ""
}libs/themes/` +
config.ui.theme.name
) }
alt="logo"
/>
);
};

return (
<>
<OxygenHeader
className="is-header"
brand={ {
logo: {
desktop: <LOGO_IMAGE />,
mobile: <LOGO_IMAGE />
desktop: <LogoImage />,
mobile: <LogoImage />
},
onClick: () =>
hasGettingStartedViewPermission &&
Expand Down Expand Up @@ -450,7 +429,7 @@
</MenuItem>
</Show>
),
<Show featureId={ FeatureGateConstants.SAAS_FEATURES_IDENTIFIER }>

Check failure on line 432 in features/admin.core.v1/components/header.tsx

View workflow job for this annotation

GitHub Actions / ⬣ ESLint (STATIC ANALYSIS) (lts/*, 8.7.4)

Missing "key" prop for element in array
<Show
when={ loginAndRegistrationFeatureConfig?.scopes?.update }
featureId={ FeatureGateConstants.PREVIEW_FEATURES_IDENTIFIER }
Expand Down
48 changes: 48 additions & 0 deletions features/admin.core.v1/components/logo-image.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com).
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import Image from "@oxygen-ui/react/Image";
import { resolveAppLogoFilePath } from "@wso2is/core/helpers";
import { StringUtils } from "@wso2is/core/utils";
import React from "react";
import { useSelector } from "react-redux";
import { ConfigReducerStateInterface } from "../models/reducer-state";
import { AppState } from "../store";

const LogoImage = () => {
const config: ConfigReducerStateInterface = useSelector((state: AppState) => state.config);

return (
<Image
src={ resolveAppLogoFilePath(
window["AppUtils"].getConfig().ui.appLogo?.defaultLogoPath ??
window["AppUtils"].getConfig().ui.appLogoPath,
`${window["AppUtils"].getConfig().clientOrigin}/` +
`${
StringUtils.removeSlashesFromPath(window["AppUtils"].getConfig().appBase) !== ""
? StringUtils.removeSlashesFromPath(window["AppUtils"].getConfig().appBase) + "/"
: ""
}libs/themes/` +
config.ui.theme.name
) }
alt="logo"
/>
);
};

export default LogoImage;
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,9 @@ export const OrganizationList: FunctionComponent<OrganizationListPropsInterface>
const handleOrganizationSwitch = async (
organization: GenericOrganization
): Promise<void> => {
document.dispatchEvent(new CustomEvent("orgSwitchStart", {
detail: { organization }
}));

let response: BasicUserInfo = null;

Expand All @@ -280,6 +283,10 @@ export const OrganizationList: FunctionComponent<OrganizationListPropsInterface>
history.push(AppConstants.getPaths().get("GETTING_STARTED"));
} catch(e) {
// TODO: Handle error
} finally {
document.dispatchEvent(new CustomEvent("orgSwitchEnd", {
detail: { organization: null }
}));
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ export const OrganizationSwitchBreadcrumb: FunctionComponent<OrganizationSwitchD
organization: GenericOrganization,
redirectToStart: boolean = true
): Promise<void> => {
document.dispatchEvent(new CustomEvent("orgSwitchStart", {
detail: { organization }
}));
let response: BasicUserInfo = null;

try {
Expand All @@ -182,6 +185,10 @@ export const OrganizationSwitchBreadcrumb: FunctionComponent<OrganizationSwitchD
}
} catch(e) {
// TODO: Handle error
} finally {
document.dispatchEvent(new CustomEvent("orgSwitchEnd", {
detail: { organization: null }
}));
}
};

Expand Down
8 changes: 8 additions & 0 deletions features/admin.organizations.v1/pages/organization-edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,10 @@ const OrganizationEditPage: FunctionComponent<OrganizationEditPagePropsInterface
* Method that handles the organization switch.
*/
const handleOrganizationSwitch = async (): Promise<void> => {
document.dispatchEvent(new CustomEvent("orgSwitchStart", {
detail: { organization }
}));

let response: BasicUserInfo = null;

try {
Expand All @@ -198,6 +202,10 @@ const OrganizationEditPage: FunctionComponent<OrganizationEditPagePropsInterface
)
})
);
} finally {
document.dispatchEvent(new CustomEvent("orgSwitchEnd", {
detail: { organization: null }
}));
}
};

Expand Down
Loading