From 5fb966996a3ad8a8aba9aaec6f9c15d2c1c8e220 Mon Sep 17 00:00:00 2001 From: Colin Dawson Date: Mon, 2 Feb 2026 23:12:52 +0000 Subject: [PATCH] htmlIsland support added, so the profile and login forms are now embedded into react controls. --- public/index.html | 8 +- public/locales/en/common.json | 1 + public/locales/en/htmlIsland.json | 8 + public/locales/fr/common.json | 1 + public/locales/fr/htmlIsland.json | 8 + src/App.tsx | 27 +- src/components/common/HtmlIsland.tsx | 181 +++++++++ src/i18n/i18n.ts | 1 + .../frame/components/ExternalLoginForm.tsx | 21 ++ .../frame/components/InternalLoginForm.tsx | 245 ++++++++++++ src/modules/frame/components/LoginForm.tsx | 249 +------------ src/modules/profile/Profile.tsx | 352 +----------------- .../profile/components/ExternalProfile.tsx | 21 ++ .../profile/components/InternalProfile.tsx | 352 ++++++++++++++++++ 14 files changed, 872 insertions(+), 603 deletions(-) create mode 100644 public/locales/en/htmlIsland.json create mode 100644 public/locales/fr/htmlIsland.json create mode 100644 src/components/common/HtmlIsland.tsx create mode 100644 src/modules/frame/components/ExternalLoginForm.tsx create mode 100644 src/modules/frame/components/InternalLoginForm.tsx create mode 100644 src/modules/profile/components/ExternalProfile.tsx create mode 100644 src/modules/profile/components/InternalProfile.tsx diff --git a/public/index.html b/public/index.html index 5c0049e..8296134 100644 --- a/public/index.html +++ b/public/index.html @@ -1,4 +1,4 @@ - + @@ -28,8 +28,12 @@ work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. --> - + e-suite + + + + diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 580898b..91806ff 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -134,6 +134,7 @@ "PressAgainToUnblock": "Press again to unblock", "PrintSpecification": "Print Specification", "Profile": "Profile", + "ProfileSaved": "Profile updated.", "ResendConfirm": "Resend Confirm", "Required": "Required", "ResetPassword": "Reset Password", diff --git a/public/locales/en/htmlIsland.json b/public/locales/en/htmlIsland.json new file mode 100644 index 0000000..9164421 --- /dev/null +++ b/public/locales/en/htmlIsland.json @@ -0,0 +1,8 @@ +{ + "island": { + "loadError": "Failed to load this section.", + "networkError": "Network error while saving.", + "serverError": "Server error while saving.", + "saveSuccess": "Saved successfully." + } +} diff --git a/public/locales/fr/common.json b/public/locales/fr/common.json index 4b57008..92ef76b 100644 --- a/public/locales/fr/common.json +++ b/public/locales/fr/common.json @@ -134,6 +134,7 @@ "PressAgainToUnblock": "Appuyez de nouveau pour débloquer", "PrintSpecification": "Imprimer la spécification", "Profile": "Profil", + "ProfileSaved": "Profil mis à jour.", "ResendConfirm": "Renvoyer la confirmation", "Required": "Requis", "ResetPassword": "Réinitialiser le mot de passe", diff --git a/public/locales/fr/htmlIsland.json b/public/locales/fr/htmlIsland.json new file mode 100644 index 0000000..1a7258f --- /dev/null +++ b/public/locales/fr/htmlIsland.json @@ -0,0 +1,8 @@ +{ + "island": { + "loadError": "Échec du chargement de cette section.", + "networkError": "Erreur réseau lors de l’enregistrement.", + "serverError": "Erreur du serveur lors de l’enregistrement.", + "saveSuccess": "Enregistré avec succès." + } +} diff --git a/src/App.tsx b/src/App.tsx index 868dad9..e71d997 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -9,7 +9,6 @@ import ForgotPassword from "./modules/frame/components/ForgotPassword"; import NotFound from "./modules/frame/components/NotFound"; import Logout from "./modules/frame/components/Logout"; import LoginForm from "./modules/frame/components/LoginForm"; -import Redirect from "./components/common/Redirect"; import Mainframe from "./modules/frame/components/Mainframe"; import EmailUserAction from "./modules/frame/components/EmailUserAction"; import { HashNavigationProvider } from "./utils/HashNavigationContext"; @@ -48,9 +47,7 @@ import { Namespaces } from "./i18n/i18n"; function GetSecureRoutes() { const { t } = useTranslation(); - const profileRoute = window.__RUNTIME_CONFIG__.EXTERNAL_LOGIN ? ( - } /> - ) : ( + const profileRoute = ( } /> - ) : ( - - - - } - /> - ); - return ( @@ -466,7 +450,14 @@ function App() {
} /> - {loginRoute} + + + + } + /> = ({ + url, + islandId, + successMessage, +}) => { + const [html, setHtml] = useState(""); + const [error, setError] = useState(null); + const containerRef = useRef(null); + const { t: tIsland } = useTranslation(Namespaces.HtmlIsland); + + // + // Load the island HTML + // + const loadIsland = useCallback(() => { + setError(null); + + fetch(url, { credentials: "include" }) + .then((r) => { + if (!r.ok) throw new Error(`Failed to load island: ${r.status}`); + return r.text(); + }) + .then((text) => setHtml(text)) + .catch(() => { + setError(tIsland("island.loadError")); + toast.error(tIsland("island.loadError")); + }); + }, [url, tIsland]); + + // + // Initial load + reload when URL changes + // + useEffect(() => { + loadIsland(); + }, [loadIsland]); + + // + // After HTML is injected, run scripts + bind form handling + // + useEffect(() => { + if (!html || !containerRef.current) return; + + const container = containerRef.current; + + // + // 1. Extract and execute