/** * ProfileCompletenessBanner * * Permanent amber banner displayed on every authenticated page when the * current company profile is missing billing-required data. Hidden when * - the profile is already complete, or * - the user is already on the edit surface for their account type * (/dashboard/profile for PERSONAL, /dashboard/company for BUSINESS), or * - the user is a Shopify-channel account (authOrigin = SHOPIFY). Shopify * manages billing profile + email verification on its side; TWWIM only * surfaces compliance documents (LegalConsentBanner stays mounted). * * Reads completeness from the global AuthenticatedUser (live, patched by * company/profile mutations). Personal-vs-business routing still reads from * useProfile() because `isPersonal` is a profile-level concern. * * (c) 2026 TWWIM UG. All rights reserved. (www.twwim.com) */ import { Link, useRouterState } from '@tanstack/react-router'; import { AlertTriangle, ArrowRight, MailCheck } from 'lucide-react'; import { AuthOrigin } from '@archer/domain'; import { useProfile } from '@/features/profile/hooks/useProfile'; import { useAuthenticatedUser } from '@/features/auth/hooks/useAuthenticatedUser'; import { useTranslation } from '@/i18n/TranslationProvider'; const FIELD_LABEL_KEYS: Record = { 'address.street': 'profileCompleteness.field.street', 'address.zip': 'profileCompleteness.field.zip', 'address.city': 'profileCompleteness.field.city', 'address.country': 'profileCompleteness.field.country', 'vatId': 'profileCompleteness.field.vatId', }; export function ProfileCompletenessBanner() { const { t } = useTranslation(); const authed = useAuthenticatedUser(); const { data: profile } = useProfile(); const routerState = useRouterState(); const currentPath = routerState.location.pathname; if (!authed) return null; // Shopify-channel users: profile + email are managed on Shopify's side. // Suppress the nag entirely; only LegalConsentBanner (compliance docs) // should appear for them. if (authed.authOrigin === AuthOrigin.SHOPIFY) return null; const needsCompletion = !authed.isProfileComplete; const needsVerify = !authed.emailVerified; if (!needsCompletion && !needsVerify) return null; const completionEditPath = profile?.isPersonal ? '/dashboard/profile' : '/dashboard/company'; // Email verification is always actionable from /dashboard/profile (Resend // button lives there). When only verification is outstanding, land the user // there regardless of PERSONAL/BUSINESS. const onCompletionEdit = needsCompletion && currentPath.startsWith(completionEditPath); const onProfile = currentPath.startsWith('/dashboard/profile'); // Hide the strip entirely while the user is already on a page that lets // them fix every outstanding issue. If only one of the two nags is active // and the user is on its home surface, suppress. if (needsCompletion && !needsVerify && onCompletionEdit) return null; if (!needsCompletion && needsVerify && onProfile) return null; const fields = authed.missingProfileFields .map(key => FIELD_LABEL_KEYS[key]) .filter(Boolean) .map(key => t(key!)); return (
{needsCompletion && (

{t('profileCompleteness.title')} {t('profileCompleteness.description')} {fields.length > 0 && {fields.join(', ')}}

)} {needsVerify && (

{t('profileCompleteness.verify.title')} {t('profileCompleteness.verify.description')}

)}
{needsCompletion ? t('profileCompleteness.cta') : t('profileCompleteness.verify.cta')}
); }