UNPKG

embed-dossier-mstr-react

Version:

This is a React component that allows you to embed a MicroStrategy Dossier in your React application.

497 lines (486 loc) 14.1 kB
// src/hooks/useCreateDashboard.ts import { useEffect as useEffect2, useRef, useState as useState2 } from "react"; // src/hooks/useLoadMstrSDK.ts import { useEffect, useState } from "react"; var useLoadMstrSDK = ({ serverUrlLibrary }) => { const [isSdkLoaded, setIsSdkLoaded] = useState(false); const [isSdkError, setIsSdkError] = useState({ isError: false, message: "" }); useEffect(() => { const loadSdk = () => { if (!window.microstrategy) { const script = document.createElement("script"); script.type = "text/javascript"; script.src = serverUrlLibrary + "/javascript/embeddinglib.js"; script.onload = () => { setIsSdkLoaded(true); }; script.onerror = () => { setIsSdkError({ isError: true, message: "Error loading the SDK" }); }; document.head.appendChild(script); } }; loadSdk(); }, [serverUrlLibrary]); return { isSdkLoaded, isSdkError }; }; // src/hooks/useCreateDashboard.ts var useCreateDashboard = ({ serverUrlLibrary, config }) => { const containerRef = useRef(null); const [dashboard, setDashboard] = useState2(null); const [isDashboardError, setIsDashboardError] = useState2(false); const { isSdkLoaded } = useLoadMstrSDK({ serverUrlLibrary }); useEffect2(() => { if (!isSdkLoaded) return; async function createDashboard() { try { const dossier = await window.microstrategy.dossier.create({ ...config, placeholder: containerRef.current }); setDashboard(dossier); } catch (error) { console.error(error); setIsDashboardError(true); } } createDashboard(); }, [isSdkLoaded]); return { dashboard, containerRef, isSdkLoaded, isDashboardError }; }; // src/utils.ts var getInfoFromUrl = (url) => { const urlParts = url.split("/").filter((part) => part !== ""); const serverUrl = urlParts[0] + "//" + urlParts[1]; const serverUrlLibrary = urlParts[0] + "//" + urlParts[1] + "/" + urlParts[2]; const libraryName = urlParts[2]; const projectId = urlParts[3]; const dossierId = urlParts[4]; return { serverUrl, serverUrlLibrary, libraryName, projectId, dossierId }; }; var getAuthToken = async ({ serverUrlLibrary }) => { const options = { method: "GET", credentials: "include", mode: "cors", headers: { "content-type": "application/json" } }; let mstrToken = ""; try { const response = await fetch(`${serverUrlLibrary}/api/auth/token`, options); if (response.ok) { const data = await response.json(); mstrToken = data.token; return mstrToken; } return response.json().then((json) => console.log(json)); } catch (error) { console.error(error); } }; var getServerUrl = (url) => { return url.split("/app/")[0]; }; // src/components/DashboardEmbed.tsx import cn from "classnames"; import { jsx } from "react/jsx-runtime"; var DashboardEmbed = ({ dossierUrl, className, style, config }) => { let serverUrlLibrary = ""; try { const urlInfo = getInfoFromUrl(dossierUrl); serverUrlLibrary = urlInfo.serverUrlLibrary; } catch (error) { console.error("Failed to parse dossier URL:", error); } const { containerRef } = useCreateDashboard({ serverUrlLibrary, config: { url: dossierUrl, ...config } }); return /* @__PURE__ */ jsx("div", { ref: containerRef, className: cn(className), style }); }; // src/hooks/useCreateLibraryPage.ts import { useEffect as useEffect3, useState as useState3, useRef as useRef2 } from "react"; var useCreateLibraryPage = ({ serverUrlLibrary, config }) => { const libraryPageRef = useRef2(null); const [libraryPage, setLibraryPage] = useState3(null); const [libraryPageState, setLibraryPageState] = useState3({ isLoading: false, error: null }); const { isSdkLoaded } = useLoadMstrSDK({ serverUrlLibrary }); const prevConfigRef = useRef2(config); useEffect3(() => { if (!isSdkLoaded) return; if (!libraryPage && !libraryPageState.isLoading) { createLibraryPage(); } }, [isSdkLoaded]); useEffect3(() => { const configChanged = JSON.stringify(config) !== JSON.stringify(prevConfigRef.current); if (libraryPage && configChanged) { createLibraryPage(); } prevConfigRef.current = config; }, [config]); const createLibraryPage = async () => { setLibraryPageState((prev) => ({ ...prev, isLoading: true, error: null })); try { const embeddingContext = await window.microstrategy.embeddingContexts.embedLibraryPage({ placeholder: libraryPageRef.current, serverUrl: serverUrlLibrary, errorHandler: (error) => { console.error("Library page error:", error); setLibraryPageState((prev) => ({ ...prev, error: error.message || "Failed to load library page" })); }, sessionErrorHandler: (error) => { console.error("Session error:", error); setLibraryPageState((prev) => ({ ...prev, error: "Session expired. Please refresh the page." })); }, ...config }); setLibraryPage(embeddingContext); } catch (error) { setLibraryPageState((prev) => ({ ...prev, error: error instanceof Error ? error.message : "Failed to create library page" })); } finally { setLibraryPageState((prev) => ({ ...prev, isLoading: false })); } }; return { libraryPage, libraryPageRef, isSdkLoaded, isLoading: libraryPageState.isLoading, error: libraryPageState.error }; }; // src/components/LibraryPageEmbed.tsx import cn2 from "classnames"; import { jsx as jsx2 } from "react/jsx-runtime"; var LibraryPageEmbed = ({ serverUrlLibrary, config, className, style }) => { const { libraryPageRef } = useCreateLibraryPage({ serverUrlLibrary, config }); return /* @__PURE__ */ jsx2("div", { ref: libraryPageRef, className: cn2(className), style }); }; // src/hooks/useCreateBotConsumptionPage.ts import { useEffect as useEffect4, useRef as useRef3, useState as useState4 } from "react"; var useCreateBotConsumptionPage = ({ serverUrlLibrary, projectId, objectId, config }) => { const botConsumptionPageRef = useRef3(null); const [botConsumptionPage, setBotConsumptionPage] = useState4(null); const [isBotConsumptionPageError, setIsBotConsumptionPageError] = useState4(false); const { isSdkLoaded, isSdkError } = useLoadMstrSDK({ serverUrlLibrary }); useEffect4(() => { if (!isSdkLoaded) return; async function createBotConsumptionPage() { try { const botConsumptionPage2 = await window.microstrategy.embeddingContexts.embedBotConsumptionPage({ ...config, placeholder: botConsumptionPageRef.current, serverUrl: serverUrlLibrary, projectId, objectId }); setBotConsumptionPage(botConsumptionPage2); } catch (error) { console.error(error); setIsBotConsumptionPageError(true); } } createBotConsumptionPage(); }, [isSdkLoaded]); return { botConsumptionPage, botConsumptionPageRef, isBotConsumptionPageError, isSdkError, isSdkLoaded }; }; // src/components/BotConsumptionPage.tsx import cn3 from "classnames"; import { jsx as jsx3 } from "react/jsx-runtime"; var BotConsumptionPage = ({ serverUrlLibrary, projectId, objectId, config, className, style }) => { const { botConsumptionPageRef } = useCreateBotConsumptionPage({ serverUrlLibrary, projectId, objectId, config }); return /* @__PURE__ */ jsx3( "div", { ref: botConsumptionPageRef, className: cn3(className), style } ); }; // src/hooks/useCreateDashboardWithAuth.ts import { useEffect as useEffect5, useState as useState5 } from "react"; var useCreateDashboardWithAuth = ({ serverUrlLibrary, placeholder, config, loginMode, username, password }) => { const [dashboard, setDashboard] = useState5(null); const [authState, setAuthState] = useState5({ isAuthenticated: false, isAuthenticating: false, error: null }); const { isSdkLoaded } = useLoadMstrSDK({ serverUrlLibrary }); useEffect5(() => { if (!isSdkLoaded || !placeholder) return; let mounted = true; const initializeDashboard = async () => { if (!mounted) return; setAuthState((prev) => ({ ...prev, isAuthenticating: true, error: null })); try { switch (loginMode) { case "standard": case "ldap": { if (!username || !password) { throw new Error( "Username and password are required for standard/LDAP authentication" ); } const standardOptions = { method: "POST", credentials: "include", mode: "cors", headers: { "content-type": "application/json" }, body: JSON.stringify({ loginMode: 1, username, password }) }; await fetch(`${serverUrlLibrary}/api/auth/login`, standardOptions); break; } case "guest": { const guestOptions = { method: "POST", credentials: "include", mode: "cors", headers: { "content-type": "application/json" }, body: JSON.stringify({ loginMode: 8 }) }; await fetch(`${serverUrlLibrary}/api/auth/login`, guestOptions); break; } case "saml": try { await window.microstrategy.auth.samlLogin(serverUrlLibrary); } catch (e) { if (e instanceof Error && e.message.includes("Failed to open a new tab")) { throw new Error( "Please enable popups to be directed to the SAML login site" ); } throw e; } break; case "oidc": try { await window.microstrategy.auth.oidcLogin(serverUrlLibrary); } catch (e) { if (e instanceof Error && e.message.includes("Failed to open a new tab")) { throw new Error( "Please enable popups to be directed to the OIDC login site" ); } throw e; } break; } const dossier = await window.microstrategy.dossier.create({ ...config, placeholder, enableCustomAuthentication: true, customAuthenticationType: window.microstrategy.dossier.CustomAuthenticationType.AUTH_TOKEN, getLoginToken: async () => { const options = { method: "GET", credentials: "include", mode: "cors", headers: { "content-type": "application/json" } }; const response = await fetch( `${serverUrlLibrary}/api/auth/token`, options ); return response.ok ? response.headers.get("x-mstr-authtoken") : null; } }); if (mounted) { setDashboard(dossier); setAuthState((prev) => ({ ...prev, isAuthenticated: true })); } } catch (error) { if (mounted) { console.error("Dashboard auth error:", error); setAuthState((prev) => ({ ...prev, error: error instanceof Error ? error.message : "Failed to initialize dashboard", isAuthenticated: false })); } } finally { if (mounted) { setAuthState((prev) => ({ ...prev, isAuthenticating: false })); } } }; initializeDashboard(); return () => { mounted = false; }; }, [ isSdkLoaded, placeholder, loginMode, serverUrlLibrary, config, username, password ]); return { dashboard, ...authState }; }; // src/components/DashboardEmbedWithAuth.tsx import cn4 from "classnames"; import { useRef as useRef4 } from "react"; import { Fragment, jsx as jsx4, jsxs } from "react/jsx-runtime"; var DashboardEmbedWithAuth = ({ dossierUrl, className, style, config, loginMode, username, password, loadingComponent = /* @__PURE__ */ jsx4("div", { children: "Loading..." }), errorComponent = (error) => /* @__PURE__ */ jsxs("div", { className: "text-center text-red-500 p-4 max-w-md", children: [ /* @__PURE__ */ jsx4("h3", { className: "text-lg font-semibold mb-2", children: "Authentication Error" }), /* @__PURE__ */ jsx4("p", { children: error }) ] }) }) => { const { serverUrlLibrary } = getInfoFromUrl(dossierUrl); const divRef = useRef4(null); const { isAuthenticating, error } = useCreateDashboardWithAuth({ serverUrlLibrary, placeholder: divRef.current, config: { url: dossierUrl, enableResponsive: true, enableCustomAuthentication: true, containerHeight: "600px", containerWidth: "100%", ...config }, loginMode, username, password }); if (isAuthenticating) { return /* @__PURE__ */ jsx4(Fragment, { children: loadingComponent }); } if (error) { return /* @__PURE__ */ jsx4(Fragment, { children: typeof errorComponent === "function" ? errorComponent(error) : errorComponent }); } return /* @__PURE__ */ jsx4( "div", { ref: divRef, className: cn4("w-full h-[600px]", className), style } ); }; export { BotConsumptionPage, DashboardEmbed, DashboardEmbedWithAuth, LibraryPageEmbed, getAuthToken, getInfoFromUrl, getServerUrl, useCreateBotConsumptionPage, useCreateDashboard, useCreateDashboardWithAuth, useCreateLibraryPage, useLoadMstrSDK };