From 0b8bbf56866137c6c08d93f86e038ee1246ef86e Mon Sep 17 00:00:00 2001 From: Jonathan Payne Date: Thu, 15 Oct 2026 23:43:41 -0400 Subject: [PATCH 1/2] OpenConceptLab/ocl_online#504 | A profile slot under the user's name for a component from the private UI package loadUserProfileExtension() loads UserProfileExtension from the optional ocl-analytics-web package, as loadUsageDashboard() does; without the package the stub's null renders nothing. UserProfile renders it under the username with the profile's user, the current user, APIService and the analytics URL. Co-Authored-By: Claude Opus 5.5 --- src/common/plugins/loader.js | 11 +++++++++++ src/common/plugins/usage-dashboard.stub.js | 1 + src/components/users/UserProfile.jsx | 12 ++++++++++++ 3 files changed, 24 insertions(+) diff --git a/src/common/plugins/loader.js b/src/common/plugins/loader.js index 213b1e7f9..4dea2f371 100644 --- a/src/common/plugins/loader.js +++ b/src/common/plugins/loader.js @@ -9,3 +9,14 @@ export async function loadUsageDashboard() { return null; } } + +// A component the private package renders under the name on a user's profile, or null without the package +export async function loadUserProfileExtension() { + try { + /*eslint no-undef: 0*/ + const mod = await import("ocl-analytics-web"); + return mod.UserProfileExtension || null; + } catch (e) { + return null; + } +} diff --git a/src/common/plugins/usage-dashboard.stub.js b/src/common/plugins/usage-dashboard.stub.js index 2f88b3d64..905fb6a44 100644 --- a/src/common/plugins/usage-dashboard.stub.js +++ b/src/common/plugins/usage-dashboard.stub.js @@ -1,2 +1,3 @@ export const UsageDashboard = null; +export const UserProfileExtension = null; export default null; diff --git a/src/components/users/UserProfile.jsx b/src/components/users/UserProfile.jsx index 7c9c22738..2a0601955 100644 --- a/src/components/users/UserProfile.jsx +++ b/src/components/users/UserProfile.jsx @@ -1,3 +1,4 @@ +/*eslint no-process-env: 0*/ import React from 'react'; import { useParams } from 'react-router-dom' import { useTranslation } from 'react-i18next' @@ -20,7 +21,10 @@ import Link from '../common/Link' import { OperationsContext } from '../app/LayoutContext'; import APIService from '../../services/APIService'; import FollowActionButton from '../common/FollowActionButton' +import { loadUserProfileExtension } from '../../common/plugins/loader' +// eslint-disable-next-line no-undef +const ANALYTICS_URL = window.ANALYTICS_API || process.env.ANALYTICS_API const UserProperty = ({icon, value, label}) => { return value ? ( @@ -45,6 +49,7 @@ const UserProfile = ({ user }) => { const { t } = useTranslation() const params = useParams() const [apiToken, setApiToken] = React.useState(undefined) + const [ProfileExtension, setProfileExtension] = React.useState(null) const { setAlert } = React.useContext(OperationsContext); const iconStyle = {fontSize: '20px', color: 'secondary.main'} const userOrgs = sortOrgs(user?.subscribed_orgs || []) @@ -60,6 +65,9 @@ const UserProfile = ({ user }) => { React.useEffect(() => { fetchAPIToken() }, [params.user]) + React.useEffect(() => { + loadUserProfileExtension().then(component => component && setProfileExtension(() => component)) + }, []) const currentUser = getCurrentUser() const isCurrentUser = Boolean(currentUser?.username && currentUser?.username == params.user) return ( @@ -71,6 +79,10 @@ const UserProfile = ({ user }) => { {user?.username} + { + ProfileExtension && + + } } value={user?.location} /> } value={user?.company} /> From 62db21de73c347eb7ea77090426939c2b69e1326 Mon Sep 17 00:00:00 2001 From: Sunny Aggarwal Date: Fri, 16 Oct 2026 12:08:07 +0530 Subject: [PATCH 2/2] OpenConceptLab/ocl_online#504 | updates to translations and cleanup --- src/common/plugins/loader.js | 19 ++++++++----------- src/components/users/UserProfile.jsx | 5 ++--- src/i18n/locales/en/translations.json | 19 +++++++++++++++++++ src/i18n/locales/es/translations.json | 19 +++++++++++++++++++ src/i18n/locales/pt-BR/translations.json | 19 +++++++++++++++++++ src/i18n/locales/zh/translations.json | 19 +++++++++++++++++++ 6 files changed, 86 insertions(+), 14 deletions(-) diff --git a/src/common/plugins/loader.js b/src/common/plugins/loader.js index 4dea2f371..94822e85e 100644 --- a/src/common/plugins/loader.js +++ b/src/common/plugins/loader.js @@ -1,22 +1,19 @@ -export async function loadUsageDashboard() { +async function loadPrivateExport(name) { try { // webpack won't try to resolve this at build time /*eslint no-undef: 0*/ const mod = await import("ocl-analytics-web"); - /*eslint no-console: 0*/ - return mod.UsageDashboard || mod.default || null; + return mod[name] || null; } catch (e) { return null; } } +export function loadUsageDashboard() { + return loadPrivateExport("UsageDashboard"); +} + // A component the private package renders under the name on a user's profile, or null without the package -export async function loadUserProfileExtension() { - try { - /*eslint no-undef: 0*/ - const mod = await import("ocl-analytics-web"); - return mod.UserProfileExtension || null; - } catch (e) { - return null; - } +export function loadUserProfileExtension() { + return loadPrivateExport("UserProfileExtension"); } diff --git a/src/components/users/UserProfile.jsx b/src/components/users/UserProfile.jsx index 2a0601955..03b005c88 100644 --- a/src/components/users/UserProfile.jsx +++ b/src/components/users/UserProfile.jsx @@ -1,4 +1,3 @@ -/*eslint no-process-env: 0*/ import React from 'react'; import { useParams } from 'react-router-dom' import { useTranslation } from 'react-i18next' @@ -23,7 +22,7 @@ import APIService from '../../services/APIService'; import FollowActionButton from '../common/FollowActionButton' import { loadUserProfileExtension } from '../../common/plugins/loader' -// eslint-disable-next-line no-undef +// eslint-disable-next-line no-undef, no-process-env const ANALYTICS_URL = window.ANALYTICS_API || process.env.ANALYTICS_API const UserProperty = ({icon, value, label}) => { @@ -81,7 +80,7 @@ const UserProfile = ({ user }) => { { ProfileExtension && - + } } value={user?.location} /> diff --git a/src/i18n/locales/en/translations.json b/src/i18n/locales/en/translations.json index c9a20d576..e19eed625 100644 --- a/src/i18n/locales/en/translations.json +++ b/src/i18n/locales/en/translations.json @@ -926,6 +926,25 @@ "profile_update_success": "Profile updated successfully", "profile_update_failure": "An error occurred while updating profile.", "copy_api_token": "Copy API token", + "plan": { + "heading": "Plan", + "community": "Community", + "community_subtitle": "Free plan", + "early_access": "Early access", + "early_access_subtitle": "Free during early access", + "pro": "Pro", + "team": "Team", + "paid_subtitle": "Paid plan", + "core": "Core", + "core_subtitle": "OCL core team: no plan limits", + "manage_billing": "Manage billing", + "see_plans": "See plans", + "view_usage": "View usage", + "view_usage_staff": "View usage (staff)", + "billing_questions": "Billing questions: {{email}}", + "staff_badge": "Staff", + "test_badge": "Test account" + }, "copy_token_success": "Copied token to your clipboard", "your": "Your", "avatar": "Avatar", diff --git a/src/i18n/locales/es/translations.json b/src/i18n/locales/es/translations.json index 5859ac78b..2325c081a 100644 --- a/src/i18n/locales/es/translations.json +++ b/src/i18n/locales/es/translations.json @@ -527,6 +527,25 @@ "success_delete": "Organización eliminada correctamente" }, "user": { + "plan": { + "heading": "Plan", + "community": "Comunidad", + "community_subtitle": "Plan gratuito", + "early_access": "Acceso anticipado", + "early_access_subtitle": "Gratis durante el acceso anticipado", + "pro": "Pro", + "team": "Equipo", + "paid_subtitle": "Plan de pago", + "core": "Core", + "core_subtitle": "Equipo central de OCL: sin límites de plan", + "manage_billing": "Gestionar facturación", + "see_plans": "Ver planes", + "view_usage": "Ver uso", + "view_usage_staff": "Ver uso (staff)", + "billing_questions": "Preguntas de facturación: {{email}}", + "staff_badge": "Staff", + "test_badge": "Cuenta de prueba" + }, "users": "Usuarios", "user": "Usuario", "my_profile": "Mi perfil", diff --git a/src/i18n/locales/pt-BR/translations.json b/src/i18n/locales/pt-BR/translations.json index 24e20d7f8..b7f3b620a 100644 --- a/src/i18n/locales/pt-BR/translations.json +++ b/src/i18n/locales/pt-BR/translations.json @@ -916,6 +916,25 @@ "profile_update_success": "Perfil atualizado com sucesso", "profile_update_failure": "Ocorreu um erro ao atualizar o perfil.", "copy_api_token": "Copiar token da API", + "plan": { + "heading": "Plano", + "community": "Comunidade", + "community_subtitle": "Plano gratuito", + "early_access": "Acesso antecipado", + "early_access_subtitle": "Gratuito durante o acesso antecipado", + "pro": "Pro", + "team": "Equipe", + "paid_subtitle": "Plano pago", + "core": "Core", + "core_subtitle": "Equipe principal da OCL: sem limites de plano", + "manage_billing": "Gerenciar cobrança", + "see_plans": "Ver planos", + "view_usage": "Ver uso", + "view_usage_staff": "Ver uso (equipe)", + "billing_questions": "Dúvidas de cobrança: {{email}}", + "staff_badge": "Equipe", + "test_badge": "Conta de teste" + }, "copy_token_success": "Token copiado para sua área de transferência", "your": "Seu", "avatar": "Avatar", diff --git a/src/i18n/locales/zh/translations.json b/src/i18n/locales/zh/translations.json index 609411ff8..52368eeef 100644 --- a/src/i18n/locales/zh/translations.json +++ b/src/i18n/locales/zh/translations.json @@ -586,6 +586,25 @@ "profile_update_success": "已成功更新个人资料", "profile_update_failure": "更新个人资料时出现错误。", "copy_api_token": "复制 API 令牌", + "plan": { + "heading": "套餐", + "community": "社区版", + "community_subtitle": "免费套餐", + "early_access": "抢先体验", + "early_access_subtitle": "抢先体验期间免费", + "pro": "Pro", + "team": "团队版", + "paid_subtitle": "付费套餐", + "core": "核心", + "core_subtitle": "OCL 核心团队:无套餐限制", + "manage_billing": "管理账单", + "see_plans": "查看套餐", + "view_usage": "查看用量", + "view_usage_staff": "查看用量(员工)", + "billing_questions": "账单问题:{{email}}", + "staff_badge": "员工", + "test_badge": "测试账户" + }, "copy_token_success": "已将令牌复制到您的粘贴板", "your": "您的", "avatar": "头像",