diff --git a/apps/compliance-portal/src/_locales/de-DE.json b/apps/compliance-portal/src/_locales/de-DE.json index 87cb15da3..5601191cb 100644 --- a/apps/compliance-portal/src/_locales/de-DE.json +++ b/apps/compliance-portal/src/_locales/de-DE.json @@ -106,5 +106,12 @@ "useThis": "{{language}} als meine Sprache verwenden", "dismiss": "Schließen" } + }, + "nda": { + "unsignedBanner": { + "message": "Unterschreiben Sie die Geheimhaltungsvereinbarung, um auf private Dokumente zuzugreifen.", + "sign": "Prüfen und unterschreiben", + "dismiss": "Schließen" + } } } diff --git a/apps/compliance-portal/src/_locales/en-US.json b/apps/compliance-portal/src/_locales/en-US.json index 04a2e59da..ac603934d 100644 --- a/apps/compliance-portal/src/_locales/en-US.json +++ b/apps/compliance-portal/src/_locales/en-US.json @@ -106,5 +106,12 @@ "pagination": { "previous": "Previous page", "next": "Next page" + }, + "nda": { + "unsignedBanner": { + "message": "Sign the NDA to access private documents.", + "sign": "Review and sign", + "dismiss": "Dismiss" + } } } diff --git a/apps/compliance-portal/src/_locales/es-ES.json b/apps/compliance-portal/src/_locales/es-ES.json index 9f792e358..f8b9f06e1 100644 --- a/apps/compliance-portal/src/_locales/es-ES.json +++ b/apps/compliance-portal/src/_locales/es-ES.json @@ -106,5 +106,12 @@ "useThis": "Usar {{language}} como mi idioma", "dismiss": "Descartar" } + }, + "nda": { + "unsignedBanner": { + "message": "Firme el acuerdo de confidencialidad para acceder a los documentos privados.", + "sign": "Revisar y firmar", + "dismiss": "Cerrar" + } } } diff --git a/apps/compliance-portal/src/_locales/fr-FR.json b/apps/compliance-portal/src/_locales/fr-FR.json index c03e76b59..9ba8a9844 100644 --- a/apps/compliance-portal/src/_locales/fr-FR.json +++ b/apps/compliance-portal/src/_locales/fr-FR.json @@ -106,5 +106,12 @@ "useThis": "Utiliser {{language}} comme ma langue", "dismiss": "Ignorer" } + }, + "nda": { + "unsignedBanner": { + "message": "Signez l'accord de confidentialité pour accéder aux documents privés.", + "sign": "Lire et signer", + "dismiss": "Fermer" + } } } diff --git a/apps/compliance-portal/src/_locales/id-ID.json b/apps/compliance-portal/src/_locales/id-ID.json index 7dd938dfd..69a00c2e5 100644 --- a/apps/compliance-portal/src/_locales/id-ID.json +++ b/apps/compliance-portal/src/_locales/id-ID.json @@ -106,5 +106,12 @@ "useThis": "Gunakan {{language}} sebagai bahasa saya", "dismiss": "Tutup" } + }, + "nda": { + "unsignedBanner": { + "message": "Tandatangani perjanjian kerahasiaan untuk mengakses dokumen pribadi.", + "sign": "Tinjau dan tanda tangani", + "dismiss": "Tutup" + } } } diff --git a/apps/compliance-portal/src/_locales/it-IT.json b/apps/compliance-portal/src/_locales/it-IT.json index a8baf0968..d93860509 100644 --- a/apps/compliance-portal/src/_locales/it-IT.json +++ b/apps/compliance-portal/src/_locales/it-IT.json @@ -106,5 +106,12 @@ "useThis": "Usa {{language}} come mia lingua", "dismiss": "Ignora" } + }, + "nda": { + "unsignedBanner": { + "message": "Firmi l'accordo di riservatezza per accedere ai documenti privati.", + "sign": "Leggi e firma", + "dismiss": "Chiudi" + } } } diff --git a/apps/compliance-portal/src/_locales/ja-JP.json b/apps/compliance-portal/src/_locales/ja-JP.json index e15eaf6c9..e150b61fe 100644 --- a/apps/compliance-portal/src/_locales/ja-JP.json +++ b/apps/compliance-portal/src/_locales/ja-JP.json @@ -106,5 +106,12 @@ "useThis": "{{language}}を自分の言語として使用", "dismiss": "閉じる" } + }, + "nda": { + "unsignedBanner": { + "message": "非公開ドキュメントにアクセスするには秘密保持契約に署名してください。", + "sign": "確認して署名", + "dismiss": "閉じる" + } } } diff --git a/apps/compliance-portal/src/_locales/ko-KR.json b/apps/compliance-portal/src/_locales/ko-KR.json index fc07107fb..fe9eac006 100644 --- a/apps/compliance-portal/src/_locales/ko-KR.json +++ b/apps/compliance-portal/src/_locales/ko-KR.json @@ -106,5 +106,12 @@ "useThis": "표시 언어({{language}})를 내 언어로 사용", "dismiss": "닫기" } + }, + "nda": { + "unsignedBanner": { + "message": "비공개 문서에 접근하려면 비밀 유지 계약에 서명하세요.", + "sign": "검토 후 서명", + "dismiss": "닫기" + } } } diff --git a/apps/compliance-portal/src/_locales/pl-PL.json b/apps/compliance-portal/src/_locales/pl-PL.json index e0203a311..30354f39b 100644 --- a/apps/compliance-portal/src/_locales/pl-PL.json +++ b/apps/compliance-portal/src/_locales/pl-PL.json @@ -106,5 +106,12 @@ "useThis": "Użyj {{language}} jako mojego języka", "dismiss": "Zamknij" } + }, + "nda": { + "unsignedBanner": { + "message": "Podpisz umowę o zachowaniu poufności, aby uzyskać dostęp do prywatnych dokumentów.", + "sign": "Przejrzyj i podpisz", + "dismiss": "Zamknij" + } } } diff --git a/apps/compliance-portal/src/_locales/pt-PT.json b/apps/compliance-portal/src/_locales/pt-PT.json index 9bfa4ad45..44285e93f 100644 --- a/apps/compliance-portal/src/_locales/pt-PT.json +++ b/apps/compliance-portal/src/_locales/pt-PT.json @@ -106,5 +106,12 @@ "useThis": "Usar {{language}} como o meu idioma", "dismiss": "Ignorar" } + }, + "nda": { + "unsignedBanner": { + "message": "Assine o acordo de confidencialidade para aceder aos documentos privados.", + "sign": "Rever e assinar", + "dismiss": "Fechar" + } } } diff --git a/apps/compliance-portal/src/_locales/tr-TR.json b/apps/compliance-portal/src/_locales/tr-TR.json index b9892b765..3fe479d42 100644 --- a/apps/compliance-portal/src/_locales/tr-TR.json +++ b/apps/compliance-portal/src/_locales/tr-TR.json @@ -106,5 +106,12 @@ "useThis": "{{language}} dilini benim dilim olarak kullan", "dismiss": "Kapat" } + }, + "nda": { + "unsignedBanner": { + "message": "Özel belgelere erişmek için gizlilik sözleşmesini imzalayın.", + "sign": "İncele ve imzala", + "dismiss": "Kapat" + } } } diff --git a/apps/compliance-portal/src/_locales/uk-UA.json b/apps/compliance-portal/src/_locales/uk-UA.json index 1db13c4b2..4cd9b0e53 100644 --- a/apps/compliance-portal/src/_locales/uk-UA.json +++ b/apps/compliance-portal/src/_locales/uk-UA.json @@ -106,5 +106,12 @@ "useThis": "Використовувати {{language}} як мою мову", "dismiss": "Закрити" } + }, + "nda": { + "unsignedBanner": { + "message": "Підпишіть NDA, щоб отримати доступ до приватних документів.", + "sign": "Переглянути та підписати", + "dismiss": "Закрити" + } } } diff --git a/apps/compliance-portal/src/_locales/zh-CN.json b/apps/compliance-portal/src/_locales/zh-CN.json index ca0e71a04..b0675a9f4 100644 --- a/apps/compliance-portal/src/_locales/zh-CN.json +++ b/apps/compliance-portal/src/_locales/zh-CN.json @@ -106,5 +106,12 @@ "useThis": "将{{language}}设为我的语言", "dismiss": "关闭" } + }, + "nda": { + "unsignedBanner": { + "message": "签署保密协议以访问私有文档。", + "sign": "查看并签署", + "dismiss": "关闭" + } } } diff --git a/apps/compliance-portal/src/components/UnsignedNDACallout/UnsignedNDACallout.tsx b/apps/compliance-portal/src/components/UnsignedNDACallout/UnsignedNDACallout.tsx new file mode 100644 index 000000000..21a25f62f --- /dev/null +++ b/apps/compliance-portal/src/components/UnsignedNDACallout/UnsignedNDACallout.tsx @@ -0,0 +1,108 @@ +// Copyright (c) 2026 Probo Inc . +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +import { FileTextIcon, XIcon } from "@phosphor-icons/react"; +import { Link } from "@probo/ui/src/v2/Button/Link"; +import { IconButton } from "@probo/ui/src/v2/IconButton/IconButton"; +import { Text } from "@probo/ui/src/v2/typography/Text"; +import { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { graphql, useFragment } from "react-relay"; + +import { useLocalizedPath } from "#/lib/i18n/useLocale"; + +import type { UnsignedNDACallout_compliancePortal$key } from "./__generated__/UnsignedNDACallout_compliancePortal.graphql"; +import { unsignedNDACallout } from "./variants"; + +const unsignedNDACalloutFragment = graphql` + fragment UnsignedNDACallout_compliancePortal on CompliancePortal { + nonDisclosureAgreement { + viewerSignature { + status + } + } + } +`; + +interface UnsignedNDACalloutProps { + compliancePortalKey: UnsignedNDACallout_compliancePortal$key; +} + +// Full-bleed notice when a signed-in user still needs to complete the portal +// NDA. Dismissed state is React-only (no localStorage/cookies). +export function UnsignedNDACallout({ compliancePortalKey }: UnsignedNDACalloutProps) { + const { t } = useTranslation(); + const localizedPath = useLocalizedPath(); + const portal = useFragment(unsignedNDACalloutFragment, compliancePortalKey); + const [dismissed, setDismissed] = useState(false); + + const signature = portal.nonDisclosureAgreement?.viewerSignature; + const needsSignature = signature != null && signature.status !== "COMPLETED"; + const visible = !dismissed && needsSignature; + + if (!visible) { + return null; + } + + const ndaHref = `${localizedPath("/nda")}?continue=${encodeURIComponent(window.location.href)}`; + const slots = unsignedNDACallout(); + + return ( + + ); +} diff --git a/apps/compliance-portal/src/components/UnsignedNDACallout/variants.ts b/apps/compliance-portal/src/components/UnsignedNDACallout/variants.ts new file mode 100644 index 000000000..77189dc9b --- /dev/null +++ b/apps/compliance-portal/src/components/UnsignedNDACallout/variants.ts @@ -0,0 +1,36 @@ +// Copyright (c) 2026 Probo Inc . +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +import { tv } from "tailwind-variants/lite"; + +// Full-bleed unsigned-NDA banner under the TopBar: message row + CTA/dismiss, +// stacking on small screens like LocaleMismatchCallout. +export const unsignedNDACallout = tv({ + slots: { + root: "flex w-full items-center gap-3 bg-gold-3 px-8 py-2.5 max-md:flex-col max-md:items-stretch max-md:gap-3 max-md:px-4 max-md:py-3", + content: "flex min-w-0 flex-1 items-start gap-2", + icon: "mt-0.5 size-4 shrink-0 text-gold-11", + message: "min-w-0 flex-1", + dismissMobile: "md:hidden", + actions: "flex shrink-0 items-center gap-2 max-md:flex-col max-md:items-stretch", + cta: "max-md:w-full", + dismissDesktop: "max-md:hidden", + }, +}); diff --git a/apps/compliance-portal/src/pages/MainLayout.tsx b/apps/compliance-portal/src/pages/MainLayout.tsx index 152d81f23..763d617f9 100644 --- a/apps/compliance-portal/src/pages/MainLayout.tsx +++ b/apps/compliance-portal/src/pages/MainLayout.tsx @@ -26,6 +26,7 @@ import { Outlet, useMatch } from "react-router"; import { LocaleMismatchCallout } from "#/components/LocaleMismatchCallout"; import { PoweredBy } from "#/components/PoweredBy/PoweredBy"; import { TopBar } from "#/components/TopBar/TopBar"; +import { UnsignedNDACallout } from "#/components/UnsignedNDACallout/UnsignedNDACallout"; import { useResumeAccessRequest } from "#/lib/auth/useResumeAccessRequest"; import { useEnsureViewerLocale } from "#/lib/i18n/useEnsureViewerLocale"; import { SubscribeDialogProvider } from "#/lib/mailingList/SubscribeDialogProvider"; @@ -39,6 +40,9 @@ export const mainLayoutQuery = graphql` ...LocaleMismatchCallout_identity ...useEnsureViewerLocale_identity } + currentCompliancePortal { + ...UnsignedNDACallout_compliancePortal + } ...TopBar_query ...SubscribeDialogProvider_query } @@ -74,6 +78,9 @@ export function MainLayout({ queryRef }: MainLayoutProps) { {data.viewer != null ? : null} + {data.viewer != null && data.currentCompliancePortal != null + ? + : null}