@burdenoff/microfe-auth 2026.530.3 → 2026.531.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/components/AuthLayout.js +12 -0
- package/dist/auth/components/AuthLayout.js.map +1 -1
- package/dist/auth/hooks/useAuthOperations.js +1 -1
- package/dist/auth/hooks/useAuthOperations.js.map +1 -1
- package/dist/auth/hooks/useContextSwitcherData.js +6 -5
- package/dist/auth/hooks/useContextSwitcherData.js.map +1 -1
- package/dist/auth/pages/RegisterPage.js +219 -190
- package/dist/auth/pages/RegisterPage.js.map +1 -1
- package/package.json +2 -2
|
@@ -21,12 +21,24 @@ var a = [
|
|
|
21
21
|
nativeName: "हिन्दी",
|
|
22
22
|
flag: "🇮🇳"
|
|
23
23
|
},
|
|
24
|
+
{
|
|
25
|
+
code: "zh",
|
|
26
|
+
name: "Chinese",
|
|
27
|
+
nativeName: "中文",
|
|
28
|
+
flag: "🇨🇳"
|
|
29
|
+
},
|
|
24
30
|
{
|
|
25
31
|
code: "es",
|
|
26
32
|
name: "Spanish",
|
|
27
33
|
nativeName: "Español",
|
|
28
34
|
flag: "🇪🇸"
|
|
29
35
|
},
|
|
36
|
+
{
|
|
37
|
+
code: "ar",
|
|
38
|
+
name: "Arabic",
|
|
39
|
+
nativeName: "العربية",
|
|
40
|
+
flag: "🇸🇦"
|
|
41
|
+
},
|
|
30
42
|
{
|
|
31
43
|
code: "fr",
|
|
32
44
|
name: "French",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuthLayout.js","names":[],"sources":["../../../src/auth/components/AuthLayout.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { LanguageSwitcher } from \"@burdenoff/fe-libs/chrome\";\nimport type { Language } from \"@burdenoff/fe-libs/chrome\";\nimport {\n useI18n,\n useAppShell,\n} from \"@burdenoff/fe-libs/shared/providers/shell\";\n\ninterface AuthLayoutProps {\n children: ReactNode;\n title: string;\n subtitle: string;\n productName?: string;\n tagline?: string;\n}\n\n/**\n * Supported languages for the auth pages\n */\nconst AUTH_LANGUAGES: Language[] = [\n { code: \"en\", name: \"English\", nativeName: \"English\", flag: \"🇺🇸\" },\n { code: \"ta\", name: \"Tamil\", nativeName: \"தமிழ்\", flag: \"🇮🇳\" },\n { code: \"hi\", name: \"Hindi\", nativeName: \"हिन्दी\", flag: \"🇮🇳\" },\n { code: \"es\", name: \"Spanish\", nativeName: \"Español\", flag: \"🇪🇸\" },\n { code: \"fr\", name: \"French\", nativeName: \"Français\", flag: \"🇫🇷\" },\n];\n\n/**\n * Professional auth layout with split-screen design\n * Left side: Branding with gradient background\n * Right side: Auth form\n */\nexport function AuthLayout({\n children,\n title,\n subtitle,\n productName,\n tagline,\n}: AuthLayoutProps) {\n const shell = useAppShell();\n const displayName = productName || shell.productName || \"Burdenoff\";\n const displayTagline = tagline || shell.tagline || \"Enterprise platform\";\n const { t, locale, setLocale } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n\n const handleLanguageChange = (language: Language) => {\n setLocale(language.code as \"en\" | \"ta\" | \"hi\" | \"es\" | \"fr\");\n };\n\n return (\n <div className=\"min-h-screen flex relative\">\n {/* Left Panel - Branding */}\n <div className=\"auth-brand-panel hidden overflow-hidden lg:flex lg:w-1/2 relative\">\n {/* Decorative Elements */}\n <div className=\"absolute inset-0 overflow-hidden\">\n <div className=\"absolute -top-40 -left-40 size-80 rounded-full bg-accent-blue/20 blur-3xl\" />\n <div className=\"absolute top-1/2 -right-20 size-96 rounded-full bg-accent-indigo/20 blur-3xl\" />\n <div className=\"absolute -bottom-40 left-1/4 size-72 rounded-full bg-accent-purple/20 blur-3xl\" />\n\n {/* Grid Pattern */}\n <div\n className=\"absolute inset-0 opacity-5\"\n style={{\n backgroundImage: `url(\"data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23ffffff' fill-opacity='1'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E\")`,\n }}\n />\n </div>\n\n {/* Content */}\n <div className=\"relative z-10 flex flex-col justify-between p-12 w-full\">\n {/* Logo */}\n <div className=\"flex items-center gap-3\">\n <div className=\"auth-brand-badge flex size-10 items-center justify-center rounded-xl\">\n <svg\n className=\"size-6 text-white\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10\"\n />\n </svg>\n </div>\n <span className=\"text-xl font-bold text-[var(--color-text-primary)]\">\n {displayName}\n </span>\n </div>\n\n {/* Main Content */}\n <div className=\"max-w-md\">\n <h1 className=\"mb-4 text-4xl font-bold leading-tight text-[var(--color-text-primary)]\">\n {displayTagline}\n </h1>\n <p className=\"auth-brand-copy text-lg leading-relaxed\">\n {tr(\n \"auth.layout.taglineDescription\",\n \"Unify your teams, projects, and workflows in one intelligent workspace. Built for scale with enterprise-grade security, real-time collaboration, and seamless integrations across your entire organization.\",\n )}\n </p>\n\n {/* Features */}\n <div className=\"mt-10 space-y-4\">\n {[\n {\n icon: \"workspace\",\n text: tr(\n \"auth.layout.featureWorkspace\",\n \"Modular workspace architecture\",\n ),\n },\n {\n icon: \"realtime\",\n text: tr(\n \"auth.layout.featureRealtime\",\n \"Real-time collaboration & sync\",\n ),\n },\n {\n icon: \"security\",\n text: tr(\n \"auth.layout.featureSecurity\",\n \"Enterprise-grade security & SSO\",\n ),\n },\n {\n icon: \"scale\",\n text: tr(\n \"auth.layout.featureScale\",\n \"Unlimited scalability & performance\",\n ),\n },\n ].map((feature) => (\n <div key={feature.icon} className=\"flex items-center gap-3\">\n <div className=\"auth-highlight-chip flex size-8 items-center justify-center rounded-lg\">\n {feature.icon === \"workspace\" && (\n <svg\n className=\"size-4 text-accent-blue\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z\"\n />\n </svg>\n )}\n {feature.icon === \"realtime\" && (\n <svg\n className=\"size-4 text-accent-blue\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M17 8h2a2 2 0 012 2v6a2 2 0 01-2 2h-2v4l-4-4H9a1.994 1.994 0 01-1.414-.586m0 0L11 14h4a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2v4l.586-.586z\"\n />\n </svg>\n )}\n {feature.icon === \"security\" && (\n <svg\n className=\"size-4 text-accent-blue\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z\"\n />\n </svg>\n )}\n {feature.icon === \"scale\" && (\n <svg\n className=\"size-4 text-accent-blue\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z\"\n />\n </svg>\n )}\n </div>\n <span className=\"auth-brand-copy text-sm font-medium\">\n {feature.text}\n </span>\n </div>\n ))}\n </div>\n </div>\n\n {/* Footer */}\n <div className=\"auth-brand-copy-muted text-sm\">\n © {new Date().getFullYear()} Burdenoff. All rights reserved.\n </div>\n </div>\n </div>\n\n {/* Right Panel - Form */}\n <main className=\"auth-shell-gradient relative flex flex-1 items-center justify-center bg-[var(--color-bg-canvas)] p-6 sm:p-12\">\n {/* Language Switcher - Top Right */}\n <div className=\"absolute top-4 right-4 sm:top-6 sm:right-6 z-10\">\n <LanguageSwitcher\n languages={AUTH_LANGUAGES}\n currentLanguage={locale}\n onLanguageChange={handleLanguageChange}\n />\n </div>\n <div className=\"w-full max-w-md\">\n {/* Mobile Logo */}\n <div className=\"lg:hidden flex items-center justify-center gap-3 mb-8\">\n <div className=\"auth-brand-badge flex size-10 items-center justify-center rounded-xl\">\n <svg\n className=\"size-6 text-white\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10\"\n />\n </svg>\n </div>\n <span className=\"text-xl font-bold text-[var(--color-text-primary)]\">\n {displayName}\n </span>\n </div>\n\n {/* Header */}\n <div className=\"text-center lg:text-left mb-8\">\n <h2 className=\"text-2xl sm:text-3xl font-bold text-[var(--color-text-primary)] mb-2\">\n {title}\n </h2>\n <p className=\"text-[var(--color-text-secondary)]\">{subtitle}</p>\n </div>\n\n {/* Form Content */}\n {children}\n </div>\n </main>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AuthLayout.js","names":[],"sources":["../../../src/auth/components/AuthLayout.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { LanguageSwitcher } from \"@burdenoff/fe-libs/chrome\";\nimport type { Language } from \"@burdenoff/fe-libs/chrome\";\nimport {\n useI18n,\n useAppShell,\n} from \"@burdenoff/fe-libs/shared/providers/shell\";\nimport type { Locale } from \"@burdenoff/fe-libs/shared/providers/shell\";\n\ninterface AuthLayoutProps {\n children: ReactNode;\n title: string;\n subtitle: string;\n productName?: string;\n tagline?: string;\n}\n\n/**\n * Supported languages for the auth pages.\n *\n * Stay in sync with the Locale union in fe-libs/.../I18nProvider.tsx\n * AND with each consumer product's seeded i18n languages. Arabic (ar)\n * and Chinese (zh) were previously missing here even though both were\n * shipped as supported locales across vibecontrols/workspaces — users\n * could pick them once inside the dashboard but not from the login\n * page picker. Now in parity.\n */\nconst AUTH_LANGUAGES: Language[] = [\n { code: \"en\", name: \"English\", nativeName: \"English\", flag: \"🇺🇸\" },\n { code: \"ta\", name: \"Tamil\", nativeName: \"தமிழ்\", flag: \"🇮🇳\" },\n { code: \"hi\", name: \"Hindi\", nativeName: \"हिन्दी\", flag: \"🇮🇳\" },\n { code: \"zh\", name: \"Chinese\", nativeName: \"中文\", flag: \"🇨🇳\" },\n { code: \"es\", name: \"Spanish\", nativeName: \"Español\", flag: \"🇪🇸\" },\n { code: \"ar\", name: \"Arabic\", nativeName: \"العربية\", flag: \"🇸🇦\" },\n { code: \"fr\", name: \"French\", nativeName: \"Français\", flag: \"🇫🇷\" },\n];\n\n/**\n * Professional auth layout with split-screen design\n * Left side: Branding with gradient background\n * Right side: Auth form\n */\nexport function AuthLayout({\n children,\n title,\n subtitle,\n productName,\n tagline,\n}: AuthLayoutProps) {\n const shell = useAppShell();\n const displayName = productName || shell.productName || \"Burdenoff\";\n const displayTagline = tagline || shell.tagline || \"Enterprise platform\";\n const { t, locale, setLocale } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n\n const handleLanguageChange = (language: Language) => {\n // AUTH_LANGUAGES intentionally lists locales (e.g. \"ar\") that are seeded\n // in consumer products but not yet present in the fe-libs Locale union.\n // The runtime I18nProvider accepts any locale code, so we cast through\n // unknown to Locale to keep the picker in parity until fe-libs widens the\n // union. Keep AUTH_LANGUAGES in sync with consumer product seeds.\n setLocale(language.code as unknown as Locale);\n };\n\n return (\n <div className=\"min-h-screen flex relative\">\n {/* Left Panel - Branding */}\n <div className=\"auth-brand-panel hidden overflow-hidden lg:flex lg:w-1/2 relative\">\n {/* Decorative Elements */}\n <div className=\"absolute inset-0 overflow-hidden\">\n <div className=\"absolute -top-40 -left-40 size-80 rounded-full bg-accent-blue/20 blur-3xl\" />\n <div className=\"absolute top-1/2 -right-20 size-96 rounded-full bg-accent-indigo/20 blur-3xl\" />\n <div className=\"absolute -bottom-40 left-1/4 size-72 rounded-full bg-accent-purple/20 blur-3xl\" />\n\n {/* Grid Pattern */}\n <div\n className=\"absolute inset-0 opacity-5\"\n style={{\n backgroundImage: `url(\"data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23ffffff' fill-opacity='1'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E\")`,\n }}\n />\n </div>\n\n {/* Content */}\n <div className=\"relative z-10 flex flex-col justify-between p-12 w-full\">\n {/* Logo */}\n <div className=\"flex items-center gap-3\">\n <div className=\"auth-brand-badge flex size-10 items-center justify-center rounded-xl\">\n <svg\n className=\"size-6 text-white\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10\"\n />\n </svg>\n </div>\n <span className=\"text-xl font-bold text-[var(--color-text-primary)]\">\n {displayName}\n </span>\n </div>\n\n {/* Main Content */}\n <div className=\"max-w-md\">\n <h1 className=\"mb-4 text-4xl font-bold leading-tight text-[var(--color-text-primary)]\">\n {displayTagline}\n </h1>\n <p className=\"auth-brand-copy text-lg leading-relaxed\">\n {tr(\n \"auth.layout.taglineDescription\",\n \"Unify your teams, projects, and workflows in one intelligent workspace. Built for scale with enterprise-grade security, real-time collaboration, and seamless integrations across your entire organization.\",\n )}\n </p>\n\n {/* Features */}\n <div className=\"mt-10 space-y-4\">\n {[\n {\n icon: \"workspace\",\n text: tr(\n \"auth.layout.featureWorkspace\",\n \"Modular workspace architecture\",\n ),\n },\n {\n icon: \"realtime\",\n text: tr(\n \"auth.layout.featureRealtime\",\n \"Real-time collaboration & sync\",\n ),\n },\n {\n icon: \"security\",\n text: tr(\n \"auth.layout.featureSecurity\",\n \"Enterprise-grade security & SSO\",\n ),\n },\n {\n icon: \"scale\",\n text: tr(\n \"auth.layout.featureScale\",\n \"Unlimited scalability & performance\",\n ),\n },\n ].map((feature) => (\n <div key={feature.icon} className=\"flex items-center gap-3\">\n <div className=\"auth-highlight-chip flex size-8 items-center justify-center rounded-lg\">\n {feature.icon === \"workspace\" && (\n <svg\n className=\"size-4 text-accent-blue\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z\"\n />\n </svg>\n )}\n {feature.icon === \"realtime\" && (\n <svg\n className=\"size-4 text-accent-blue\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M17 8h2a2 2 0 012 2v6a2 2 0 01-2 2h-2v4l-4-4H9a1.994 1.994 0 01-1.414-.586m0 0L11 14h4a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2v4l.586-.586z\"\n />\n </svg>\n )}\n {feature.icon === \"security\" && (\n <svg\n className=\"size-4 text-accent-blue\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z\"\n />\n </svg>\n )}\n {feature.icon === \"scale\" && (\n <svg\n className=\"size-4 text-accent-blue\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z\"\n />\n </svg>\n )}\n </div>\n <span className=\"auth-brand-copy text-sm font-medium\">\n {feature.text}\n </span>\n </div>\n ))}\n </div>\n </div>\n\n {/* Footer */}\n <div className=\"auth-brand-copy-muted text-sm\">\n © {new Date().getFullYear()} Burdenoff. All rights reserved.\n </div>\n </div>\n </div>\n\n {/* Right Panel - Form */}\n <main className=\"auth-shell-gradient relative flex flex-1 items-center justify-center bg-[var(--color-bg-canvas)] p-6 sm:p-12\">\n {/* Language Switcher - Top Right */}\n <div className=\"absolute top-4 right-4 sm:top-6 sm:right-6 z-10\">\n <LanguageSwitcher\n languages={AUTH_LANGUAGES}\n currentLanguage={locale}\n onLanguageChange={handleLanguageChange}\n />\n </div>\n <div className=\"w-full max-w-md\">\n {/* Mobile Logo */}\n <div className=\"lg:hidden flex items-center justify-center gap-3 mb-8\">\n <div className=\"auth-brand-badge flex size-10 items-center justify-center rounded-xl\">\n <svg\n className=\"size-6 text-white\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10\"\n />\n </svg>\n </div>\n <span className=\"text-xl font-bold text-[var(--color-text-primary)]\">\n {displayName}\n </span>\n </div>\n\n {/* Header */}\n <div className=\"text-center lg:text-left mb-8\">\n <h2 className=\"text-2xl sm:text-3xl font-bold text-[var(--color-text-primary)] mb-2\">\n {title}\n </h2>\n <p className=\"text-[var(--color-text-secondary)]\">{subtitle}</p>\n </div>\n\n {/* Form Content */}\n {children}\n </div>\n </main>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -7,7 +7,7 @@ import { useApolloClient as h, useMutation as g, useQuery as _ } from "@apollo/c
|
|
|
7
7
|
import { useEventBus as v } from "@burdenoff/fe-libs/shared/events";
|
|
8
8
|
//#region src/auth/hooks/useAuthOperations.ts
|
|
9
9
|
function y(e, t) {
|
|
10
|
-
return e.includes("email or username already exists") || t === "BAD_USER_INPUT" ? "An account with this email or username already exists. Please sign in or use a different email." : t === "TENANT_CREATION_FAILED" ? "We couldn't finish setting up your account. Please try again in a few moments. If this persists, contact support." : e.includes("Failed to fetch") || e.includes("NetworkError") || e.includes("Load failed") ? "Unable to reach the server. Please check your internet connection and try again." : e.includes("CORS") || e.includes("cross-origin") ? "Unable to reach the server. Please try again or contact support." : t === "INTERNAL_SERVER_ERROR" ? "Something went wrong on our end. Please try again in a few moments." : e && e !== "Sign up failed" ? e : "Sign up failed. Please try again.";
|
|
10
|
+
return t === "ACCOUNT_DEACTIVATED" ? "This email belongs to a deactivated account. You can reactivate it instead of signing up again." : t === "ACCOUNT_REMOVED" ? "This account was removed by an administrator. Please contact support to restore it." : t === "ACCOUNT_SUSPENDED" ? "This account is suspended. Please contact support." : e.includes("email or username already exists") || t === "BAD_USER_INPUT" ? "An account with this email or username already exists. Please sign in or use a different email." : t === "TENANT_CREATION_FAILED" ? "We couldn't finish setting up your account. Please try again in a few moments. If this persists, contact support." : e.includes("Failed to fetch") || e.includes("NetworkError") || e.includes("Load failed") ? "Unable to reach the server. Please check your internet connection and try again." : e.includes("CORS") || e.includes("cross-origin") ? "Unable to reach the server. Please try again or contact support." : t === "INTERNAL_SERVER_ERROR" ? "Something went wrong on our end. Please try again in a few moments." : e && e !== "Sign up failed" ? e : "Sign up failed. Please try again.";
|
|
11
11
|
}
|
|
12
12
|
function b(e) {
|
|
13
13
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAuthOperations.js","names":[],"sources":["../../../src/auth/hooks/useAuthOperations.ts"],"sourcesContent":["import { useEffect, useRef } from \"react\";\nimport { useMutation, useQuery, useApolloClient } from \"@apollo/client/react\";\nimport { useEventBus } from \"@burdenoff/fe-libs/shared/events\";\nimport {\n loginProfile,\n getProfiles,\n getProfileTokens,\n getProfileUser,\n type ProfileUser,\n} from \"@burdenoff/fe-libs/shared/providers/shell/ProfileStorageManager\";\nimport { useAuthStore } from \"../store/authStore\";\nimport { useAuth } from \"../providers/AuthProvider\";\nimport {\n SignInDocument,\n SignUpDocument,\n SignOutDocument,\n RefreshTokenDocument,\n CurrentUserDocument,\n VerifyEmailDocument,\n ResendVerificationEmailDocument,\n} from \"../../generated/global-operations\";\nimport type {\n SignInInput,\n SignUpInput,\n User,\n AuthPayload,\n AuthResult,\n} from \"../types\";\n\n// =============================================================================\n// ERROR MAPPING HELPERS\n// =============================================================================\n\n/**\n * Map raw backend error messages to user-friendly descriptions.\n * Keeps a single source of truth for all sign-up error copy.\n */\nfunction mapSignUpError(rawMessage: string, errorCode?: string): string {\n // Exact matches from the backend\n if (\n rawMessage.includes(\"email or username already exists\") ||\n errorCode === \"BAD_USER_INPUT\"\n ) {\n return \"An account with this email or username already exists. Please sign in or use a different email.\";\n }\n\n if (errorCode === \"TENANT_CREATION_FAILED\") {\n return \"We couldn't finish setting up your account. Please try again in a few moments. If this persists, contact support.\";\n }\n\n // Network / fetch failures (Apollo wraps these)\n if (\n rawMessage.includes(\"Failed to fetch\") ||\n rawMessage.includes(\"NetworkError\") ||\n rawMessage.includes(\"Load failed\")\n ) {\n return \"Unable to reach the server. Please check your internet connection and try again.\";\n }\n\n // CORS or cross-origin issues\n if (rawMessage.includes(\"CORS\") || rawMessage.includes(\"cross-origin\")) {\n return \"Unable to reach the server. Please try again or contact support.\";\n }\n\n // Generic server errors\n if (errorCode === \"INTERNAL_SERVER_ERROR\") {\n return \"Something went wrong on our end. Please try again in a few moments.\";\n }\n\n // Pass through any already-descriptive message, or fall back\n if (rawMessage && rawMessage !== \"Sign up failed\") {\n return rawMessage;\n }\n\n return \"Sign up failed. Please try again.\";\n}\n\nfunction setShellSsoCookie(userId: string): void {\n try {\n const hostname = window.location.hostname;\n const domain = hostname.endsWith(\".local.burdenoff.com\")\n ? \".local.burdenoff.com\"\n : undefined;\n const domainPart = domain ? `; domain=${domain}` : \"\";\n const securePart = window.location.protocol === \"https:\" ? \"; secure\" : \"\";\n const ssoValue = JSON.stringify({\n id: userId,\n origin: window.location.origin,\n });\n document.cookie = `bf-sso=${encodeURIComponent(ssoValue)}${domainPart}; path=/; samesite=lax${securePart}; max-age=86400`;\n document.cookie = `bf-sso-profile=${encodeURIComponent(ssoValue)}${domainPart}; path=/; samesite=lax${securePart}; max-age=86400`;\n } catch {\n // Best effort only.\n }\n}\n\nfunction toProfileUser(user: User): ProfileUser {\n return { ...user };\n}\n\ninterface GraphQLErrorLike {\n message?: string;\n extensions?: Record<string, unknown>;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction asGraphQLErrorArray(value: unknown): GraphQLErrorLike[] {\n if (!Array.isArray(value)) return [];\n return value.filter((item): item is GraphQLErrorLike => isRecord(item));\n}\n\nfunction getGraphQLErrors(error: unknown): GraphQLErrorLike[] {\n if (!isRecord(error)) return [];\n const directErrors = asGraphQLErrorArray(error.errors);\n if (directErrors.length > 0) return directErrors;\n return asGraphQLErrorArray(error.graphQLErrors);\n}\n\nfunction getUnknownErrorMessage(error: unknown): string | undefined {\n if (error instanceof Error) return error.message;\n if (isRecord(error) && typeof error.message === \"string\") {\n return error.message;\n }\n return undefined;\n}\n\nfunction getGraphQLErrorCode(\n error: GraphQLErrorLike | undefined,\n): string | null {\n const code = error?.extensions?.code;\n return typeof code === \"string\" ? code : null;\n}\n\n// =============================================================================\n// GRAPHQL RESPONSE INTERFACES (Apollo Client 4.x)\n// =============================================================================\n\ninterface SignInResponse {\n signIn: AuthPayload;\n}\n\ninterface SignInVariables {\n input: SignInInput;\n}\n\ninterface SignUpResponse {\n signUp: AuthPayload;\n}\n\ninterface SignUpVariables {\n input: SignUpInput;\n}\n\ninterface SignOutResponse {\n signOut: boolean;\n}\n\ninterface RefreshTokenResponse {\n refreshToken: AuthPayload;\n}\n\ninterface RefreshTokenVariables {\n token: string;\n}\n\ninterface CurrentUserResponse {\n currentUser: User | null;\n}\n\ninterface UseAuthOperationsOptions {\n disableCurrentUserQuery?: boolean;\n}\n\ninterface VerifyEmailResponse {\n verifyEmail: {\n success: boolean;\n message: string;\n };\n}\n\ninterface VerifyEmailVariables {\n token: string;\n}\n\ninterface ResendVerificationEmailResponse {\n resendVerificationEmail: {\n success: boolean;\n message: string;\n };\n}\n\ninterface ResendVerificationEmailVariables {\n email: string;\n}\n\n// GraphQL Operations\n\n// SignIn mutation with MFA support\n\n/**\n * Hook for authentication operations\n * Uses Zustand v5 pattern: selectors for state, getState() for actions\n */\nexport function useAuthOperations(options: UseAuthOperationsOptions = {}) {\n // Use selectors for reactive state values\n const isLoading = useAuthStore((state) => state.isLoading);\n const error = useAuthStore((state) => state.error);\n const errorCode = useAuthStore((state) => state.errorCode);\n const isAuthenticated = useAuthStore((state) => state.isAuthenticated);\n const user = useAuthStore((state) => state.user);\n const accessToken = useAuthStore((state) => state.accessToken);\n const disableCurrentUserQuery = options.disableCurrentUserQuery ?? false;\n\n // Track whether signIn just completed — skip the CurrentUser refetch\n // because signIn already returns the full user object.\n const loginJustCompletedRef = useRef(false);\n\n const context = useAuth();\n const eventBus = useEventBus();\n const apolloClient = useApolloClient();\n const [signInMutation] = useMutation<SignInResponse, SignInVariables>(\n SignInDocument,\n );\n const [signUpMutation] = useMutation<SignUpResponse, SignUpVariables>(\n SignUpDocument,\n );\n const [signOutMutation] = useMutation<SignOutResponse>(SignOutDocument);\n const [refreshTokenMutation] = useMutation<\n RefreshTokenResponse,\n RefreshTokenVariables\n >(RefreshTokenDocument);\n const [verifyEmailMutation] = useMutation<\n VerifyEmailResponse,\n VerifyEmailVariables\n >(VerifyEmailDocument);\n const [resendVerificationMutation] = useMutation<\n ResendVerificationEmailResponse,\n ResendVerificationEmailVariables\n >(ResendVerificationEmailDocument);\n\n /**\n * Sign in with email/username and password\n * Uses signIn mutation which returns correct token format (with 'sub' field)\n */\n const signIn = async (input: SignInInput): Promise<AuthResult> => {\n const store = useAuthStore.getState();\n\n try {\n store.setLoading(true);\n store.setError(null);\n\n // rememberMe is sent to the backend so the server can issue\n // longer-lived refresh tokens (30 days instead of 7 days)\n\n // Use signIn mutation (returns correct token format with 'sub' field)\n const result = await signInMutation({\n variables: { input },\n // Override client-level errorPolicy to ensure errors are thrown\n // and caught by our catch block for proper error message extraction\n errorPolicy: \"none\",\n });\n\n // If we reach here, the mutation succeeded (no errors thrown)\n // Check if data.signIn is present\n if (!result.data || !result.data.signIn) {\n const errorMessage =\n \"Sign in failed. Please check your credentials and try again.\";\n store.setError(errorMessage);\n return {\n success: false,\n error: errorMessage,\n } as AuthResult;\n }\n\n const data = result.data;\n\n const authResult = data.signIn;\n\n // Check if MFA is required\n if (authResult.requiresMFA && authResult.mfaChallengeId) {\n store.setMfaRequired(true, authResult.mfaChallengeId);\n\n // Return success but with requiresMFA flag\n return {\n success: true,\n requiresMFA: true,\n mfaChallengeId: authResult.mfaChallengeId,\n metadata: {\n mfaMethod: authResult.mfaMethod,\n },\n } as AuthResult;\n }\n\n // Normal login without MFA - store the authentication result\n if (\n authResult.user &&\n authResult.accessToken &&\n authResult.refreshToken\n ) {\n // Signal that user data is fresh from signIn — skip CurrentUser refetch.\n loginJustCompletedRef.current = true;\n store.setUser(authResult.user);\n store.setAuthTokens(authResult.accessToken, authResult.refreshToken);\n store.setAuthenticated(true);\n store.setMfaRequired(false);\n\n const expiresAt = authResult.expiresIn\n ? Date.now() + authResult.expiresIn * 1000\n : undefined;\n loginProfile(toProfileUser(authResult.user), {\n accessToken: authResult.accessToken,\n refreshToken: authResult.refreshToken,\n expiresAt,\n });\n setShellSsoCookie(authResult.user.id);\n\n eventBus.emit(\n \"auth.signed_in\",\n {\n userId: authResult.user.id,\n tenantId: authResult.user.tenantId,\n workspaceId: authResult.user.defaultWorkspaceId,\n mfaEnabled: authResult.user.mfaEnabled,\n emailVerified: authResult.user.emailVerified,\n },\n { component: \"useAuthOperations\" },\n );\n\n context.onAuthSuccess?.(\n authResult.user,\n authResult.accessToken,\n authResult.refreshToken,\n );\n\n // Convert AuthPayload to AuthResult for consistent return type\n return {\n success: true,\n user: authResult.user,\n token: authResult.accessToken,\n refreshToken: authResult.refreshToken,\n expiresIn: authResult.expiresIn,\n requiresMFA: false,\n } as AuthResult;\n } else {\n const errorMsg = \"Login failed: Invalid response from server\";\n store.setError(errorMsg);\n return {\n success: false,\n error: errorMsg,\n } as AuthResult;\n }\n } catch (err: unknown) {\n // Extract error message and code from GraphQL error responses.\n // The backend sends user-friendly messages in gqlError.message and\n // structured error codes in gqlError.extensions.code (e.g., ACCOUNT_DELETED,\n // ACCOUNT_SUSPENDED, ACCOUNT_INACTIVE, OAUTH_ONLY_ACCOUNT, EMAIL_NOT_VERIFIED,\n // UNAUTHENTICATED, INTERNAL_SERVER_ERROR).\n // IMPORTANT: Never use gqlError.extensions.originalError — it may contain\n // internal details that should not be shown to users.\n let message = \"Sign in failed\";\n let errorCode: string | null = null;\n const graphQLErrors = getGraphQLErrors(err);\n if (graphQLErrors.length > 0) {\n // Apollo v4 CombinedGraphQLErrors\n // Apollo v3 ApolloError — use message only, never originalError\n const gqlError = graphQLErrors[0];\n message = gqlError.message || message;\n errorCode = getGraphQLErrorCode(gqlError);\n } else {\n message = getUnknownErrorMessage(err) ?? message;\n }\n\n console.error(\n \"[SignIn] Error:\",\n message,\n errorCode ? `(${errorCode})` : \"\",\n );\n store.setError(message, errorCode);\n return {\n success: false,\n error: message,\n } as AuthResult;\n } finally {\n store.setLoading(false);\n }\n };\n\n /**\n * Sign up new user\n */\n const signUp = async (input: SignUpInput) => {\n const store = useAuthStore.getState();\n\n try {\n store.setLoading(true);\n store.setError(null);\n\n const mutationResult = await signUpMutation({\n variables: { input },\n });\n\n // With errorPolicy: 'all', errors are returned in result, not thrown\n // Check for GraphQL errors first\n const graphQLErrors = getGraphQLErrors(mutationResult);\n if (graphQLErrors.length > 0) {\n const errorMessage = graphQLErrors[0].message ?? \"Sign up failed\";\n console.error(\"[SignUp] GraphQL error:\", errorMessage);\n store.setError(errorMessage);\n throw new Error(errorMessage);\n }\n\n // Check if data.signUp is null (indicates GraphQL error without explicit error)\n if (!mutationResult.data || !mutationResult.data.signUp) {\n const errorMessage = \"Sign up failed: No response from server\";\n console.error(\"[SignUp] No data:\", errorMessage);\n store.setError(errorMessage);\n throw new Error(errorMessage);\n }\n\n const result = mutationResult.data.signUp;\n\n // Check if user was created\n if (!result.user) {\n throw new Error(\"Sign up failed: No user data returned\");\n }\n\n // Check if email verification is required (empty tokens)\n if (\n !result.accessToken ||\n !result.refreshToken ||\n result.accessToken === \"\" ||\n result.refreshToken === \"\"\n ) {\n // User created successfully but needs to verify email\n // Don't set authenticated state, but store the user for display\n store.setUser(result.user);\n store.setAuthenticated(false);\n\n // Return special result indicating email verification needed\n return {\n ...result,\n requiresEmailVerification: true,\n };\n }\n\n // Normal sign up with immediate access (tokens provided)\n store.setUser(result.user);\n store.setAuthTokens(result.accessToken, result.refreshToken);\n store.setAuthenticated(true);\n\n eventBus.emit(\n \"auth.signed_up\",\n {\n userId: result.user.id,\n tenantId: result.user.tenantId,\n workspaceId: result.user.defaultWorkspaceId,\n mfaEnabled: result.user.mfaEnabled,\n emailVerified: result.user.emailVerified,\n },\n { component: \"useAuthOperations\" },\n );\n\n context.onAuthSuccess?.(\n result.user,\n result.accessToken,\n result.refreshToken,\n );\n\n return result;\n } catch (err: unknown) {\n // Extract error message from Apollo error or regular error\n let message = \"Sign up failed. Please try again.\";\n let errorCode: string | undefined;\n\n const graphQLErrors = getGraphQLErrors(err);\n if (graphQLErrors.length > 0) {\n const gqlError = graphQLErrors[0];\n errorCode = getGraphQLErrorCode(gqlError) ?? undefined;\n const rawMessage = gqlError.message ?? \"\";\n\n // Map backend error codes to user-friendly messages\n message = mapSignUpError(rawMessage, errorCode);\n } else {\n const rawMessage = getUnknownErrorMessage(err);\n if (rawMessage) {\n message = mapSignUpError(rawMessage);\n }\n }\n\n store.setError(message, errorCode ?? null);\n throw new Error(message);\n } finally {\n store.setLoading(false);\n }\n };\n\n /**\n * Sign out current session\n */\n const signOut = async () => {\n const store = useAuthStore.getState();\n const signedOutUser = store.user;\n\n try {\n await signOutMutation();\n } catch (err) {\n console.error(\"Sign out failed:\", err);\n }\n\n // Always clear Apollo cache so stale user data doesn't survive re-login\n await apolloClient.clearStore();\n store.clearAuth();\n eventBus.emit(\n \"auth.signed_out\",\n {\n userId: signedOutUser?.id,\n tenantId: signedOutUser?.tenantId,\n workspaceId: signedOutUser?.defaultWorkspaceId,\n },\n { component: \"useAuthOperations\" },\n );\n\n // If other profiles exist after clearing the current one, switch to the first\n // remaining profile instead of fully logging out.\n const remainingProfiles = getProfiles().filter(\n (p) => p.id !== signedOutUser?.id,\n );\n if (remainingProfiles.length > 0) {\n const nextProfile = remainingProfiles[0];\n const nextTokens = getProfileTokens(nextProfile.id);\n const nextUser = getProfileUser(nextProfile.id);\n if (nextTokens?.accessToken && nextUser) {\n // Switch to next profile instead of fully logging out\n store.loadFromProfile(nextProfile.id);\n return;\n }\n }\n\n context.onLogout?.();\n };\n\n /**\n * Refresh access token\n */\n const refreshToken = async () => {\n const store = useAuthStore.getState();\n\n try {\n const token = store.refreshToken;\n if (!token) throw new Error(\"No refresh token available\");\n\n const { data } = await refreshTokenMutation({\n variables: { token },\n });\n\n const result = data!.refreshToken;\n\n // Refresh token should always return valid tokens and user\n if (!result.accessToken || !result.refreshToken || !result.user) {\n throw new Error(\"Token refresh failed: Invalid response from server\");\n }\n\n store.setAuthTokens(result.accessToken, result.refreshToken);\n store.setUser(result.user);\n\n eventBus.emit(\n \"auth.token_refreshed\",\n {\n userId: result.user.id,\n tenantId: result.user.tenantId,\n workspaceId: result.user.defaultWorkspaceId,\n mfaEnabled: result.user.mfaEnabled,\n emailVerified: result.user.emailVerified,\n },\n { component: \"useAuthOperations\" },\n );\n\n return result;\n } catch (err) {\n // Refresh failed, logout user\n eventBus.emit(\n \"auth.token_refresh_failed\",\n {\n userId: store.user?.id,\n tenantId: store.user?.tenantId,\n workspaceId: store.user?.defaultWorkspaceId,\n reason: err instanceof Error ? err.message : \"Token refresh failed\",\n },\n { component: \"useAuthOperations\" },\n );\n store.clearAuth();\n throw err;\n }\n };\n\n /**\n * Get current user.\n * Skip when:\n * - Not authenticated\n * - Login just completed (user data is already fresh from signIn response)\n *\n * The loginJustCompletedRef is cleared after the first successful fetch\n * or on the next render cycle after navigation.\n */\n const { data: currentUserData, refetch: refetchCurrentUser } =\n useQuery<CurrentUserResponse>(CurrentUserDocument, {\n skip:\n disableCurrentUserQuery ||\n !isAuthenticated ||\n loginJustCompletedRef.current,\n fetchPolicy: \"network-only\",\n });\n\n // Handle currentUser data updates (replaces onCompleted callback)\n useEffect(() => {\n if (currentUserData?.currentUser) {\n useAuthStore.getState().setUser(currentUserData.currentUser);\n }\n }, [currentUserData]);\n\n // Clear loginJustCompletedRef after navigation away from login page\n // so subsequent CurrentUser queries work normally (e.g., profile page).\n useEffect(() => {\n if (loginJustCompletedRef.current && isAuthenticated) {\n const timer = setTimeout(() => {\n loginJustCompletedRef.current = false;\n }, 2000);\n return () => clearTimeout(timer);\n }\n }, [isAuthenticated]);\n\n /**\n * Verify email with token\n * Returns { success, message, email } on success\n */\n const verifyEmail = async (\n token: string,\n ): Promise<{ success: boolean; message: string; email?: string }> => {\n const store = useAuthStore.getState();\n\n try {\n store.setLoading(true);\n const { data } = await verifyEmailMutation({ variables: { token } });\n\n if (data!.verifyEmail.success && store.user) {\n store.setUser({ ...store.user, emailVerified: true });\n }\n\n return data!.verifyEmail;\n } catch (err) {\n const message =\n err instanceof Error ? err.message : \"Email verification failed\";\n store.setError(message);\n throw err;\n } finally {\n store.setLoading(false);\n }\n };\n\n /**\n * Resend verification email\n */\n const resendVerification = async (email: string) => {\n try {\n const { data } = await resendVerificationMutation({\n variables: { email },\n });\n return data!.resendVerificationEmail;\n } catch (err) {\n console.error(\"Failed to resend verification email:\", err);\n throw err;\n }\n };\n\n // Legacy aliases for backwards compatibility\n const login = signIn;\n const register = signUp;\n const logout = signOut;\n\n return {\n // New API\n signIn,\n signUp,\n signOut,\n refreshToken,\n verifyEmail,\n resendVerification,\n refetchCurrentUser,\n // Legacy aliases\n login,\n register,\n logout,\n // State (reactive via selectors)\n isLoading,\n error,\n errorCode,\n isAuthenticated,\n user,\n accessToken,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useAuthOperations.js","names":[],"sources":["../../../src/auth/hooks/useAuthOperations.ts"],"sourcesContent":["import { useEffect, useRef } from \"react\";\nimport { useMutation, useQuery, useApolloClient } from \"@apollo/client/react\";\nimport { useEventBus } from \"@burdenoff/fe-libs/shared/events\";\nimport {\n loginProfile,\n getProfiles,\n getProfileTokens,\n getProfileUser,\n type ProfileUser,\n} from \"@burdenoff/fe-libs/shared/providers/shell/ProfileStorageManager\";\nimport { useAuthStore } from \"../store/authStore\";\nimport { useAuth } from \"../providers/AuthProvider\";\nimport {\n SignInDocument,\n SignUpDocument,\n SignOutDocument,\n RefreshTokenDocument,\n CurrentUserDocument,\n VerifyEmailDocument,\n ResendVerificationEmailDocument,\n} from \"../../generated/global-operations\";\nimport type {\n SignInInput,\n SignUpInput,\n User,\n AuthPayload,\n AuthResult,\n} from \"../types\";\n\n// =============================================================================\n// ERROR MAPPING HELPERS\n// =============================================================================\n\n/**\n * Map raw backend error messages to user-friendly descriptions.\n * Keeps a single source of truth for all sign-up error copy.\n */\nfunction mapSignUpError(rawMessage: string, errorCode?: string): string {\n // Non-active accounts on this email — route the user to the right recovery\n // path. RegisterPage reads the errorCode to render the matching CTA.\n if (errorCode === \"ACCOUNT_DEACTIVATED\") {\n return \"This email belongs to a deactivated account. You can reactivate it instead of signing up again.\";\n }\n if (errorCode === \"ACCOUNT_REMOVED\") {\n return \"This account was removed by an administrator. Please contact support to restore it.\";\n }\n if (errorCode === \"ACCOUNT_SUSPENDED\") {\n return \"This account is suspended. Please contact support.\";\n }\n\n // Exact matches from the backend\n if (\n rawMessage.includes(\"email or username already exists\") ||\n errorCode === \"BAD_USER_INPUT\"\n ) {\n return \"An account with this email or username already exists. Please sign in or use a different email.\";\n }\n\n if (errorCode === \"TENANT_CREATION_FAILED\") {\n return \"We couldn't finish setting up your account. Please try again in a few moments. If this persists, contact support.\";\n }\n\n // Network / fetch failures (Apollo wraps these)\n if (\n rawMessage.includes(\"Failed to fetch\") ||\n rawMessage.includes(\"NetworkError\") ||\n rawMessage.includes(\"Load failed\")\n ) {\n return \"Unable to reach the server. Please check your internet connection and try again.\";\n }\n\n // CORS or cross-origin issues\n if (rawMessage.includes(\"CORS\") || rawMessage.includes(\"cross-origin\")) {\n return \"Unable to reach the server. Please try again or contact support.\";\n }\n\n // Generic server errors\n if (errorCode === \"INTERNAL_SERVER_ERROR\") {\n return \"Something went wrong on our end. Please try again in a few moments.\";\n }\n\n // Pass through any already-descriptive message, or fall back\n if (rawMessage && rawMessage !== \"Sign up failed\") {\n return rawMessage;\n }\n\n return \"Sign up failed. Please try again.\";\n}\n\nfunction setShellSsoCookie(userId: string): void {\n try {\n const hostname = window.location.hostname;\n const domain = hostname.endsWith(\".local.burdenoff.com\")\n ? \".local.burdenoff.com\"\n : undefined;\n const domainPart = domain ? `; domain=${domain}` : \"\";\n const securePart = window.location.protocol === \"https:\" ? \"; secure\" : \"\";\n const ssoValue = JSON.stringify({\n id: userId,\n origin: window.location.origin,\n });\n document.cookie = `bf-sso=${encodeURIComponent(ssoValue)}${domainPart}; path=/; samesite=lax${securePart}; max-age=86400`;\n document.cookie = `bf-sso-profile=${encodeURIComponent(ssoValue)}${domainPart}; path=/; samesite=lax${securePart}; max-age=86400`;\n } catch {\n // Best effort only.\n }\n}\n\nfunction toProfileUser(user: User): ProfileUser {\n return { ...user };\n}\n\ninterface GraphQLErrorLike {\n message?: string;\n extensions?: Record<string, unknown>;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction asGraphQLErrorArray(value: unknown): GraphQLErrorLike[] {\n if (!Array.isArray(value)) return [];\n return value.filter((item): item is GraphQLErrorLike => isRecord(item));\n}\n\nfunction getGraphQLErrors(error: unknown): GraphQLErrorLike[] {\n if (!isRecord(error)) return [];\n const directErrors = asGraphQLErrorArray(error.errors);\n if (directErrors.length > 0) return directErrors;\n return asGraphQLErrorArray(error.graphQLErrors);\n}\n\nfunction getUnknownErrorMessage(error: unknown): string | undefined {\n if (error instanceof Error) return error.message;\n if (isRecord(error) && typeof error.message === \"string\") {\n return error.message;\n }\n return undefined;\n}\n\nfunction getGraphQLErrorCode(\n error: GraphQLErrorLike | undefined,\n): string | null {\n const code = error?.extensions?.code;\n return typeof code === \"string\" ? code : null;\n}\n\n// =============================================================================\n// GRAPHQL RESPONSE INTERFACES (Apollo Client 4.x)\n// =============================================================================\n\ninterface SignInResponse {\n signIn: AuthPayload;\n}\n\ninterface SignInVariables {\n input: SignInInput;\n}\n\ninterface SignUpResponse {\n signUp: AuthPayload;\n}\n\ninterface SignUpVariables {\n input: SignUpInput;\n}\n\ninterface SignOutResponse {\n signOut: boolean;\n}\n\ninterface RefreshTokenResponse {\n refreshToken: AuthPayload;\n}\n\ninterface RefreshTokenVariables {\n token: string;\n}\n\ninterface CurrentUserResponse {\n currentUser: User | null;\n}\n\ninterface UseAuthOperationsOptions {\n disableCurrentUserQuery?: boolean;\n}\n\ninterface VerifyEmailResponse {\n verifyEmail: {\n success: boolean;\n message: string;\n };\n}\n\ninterface VerifyEmailVariables {\n token: string;\n}\n\ninterface ResendVerificationEmailResponse {\n resendVerificationEmail: {\n success: boolean;\n message: string;\n };\n}\n\ninterface ResendVerificationEmailVariables {\n email: string;\n}\n\n// GraphQL Operations\n\n// SignIn mutation with MFA support\n\n/**\n * Hook for authentication operations\n * Uses Zustand v5 pattern: selectors for state, getState() for actions\n */\nexport function useAuthOperations(options: UseAuthOperationsOptions = {}) {\n // Use selectors for reactive state values\n const isLoading = useAuthStore((state) => state.isLoading);\n const error = useAuthStore((state) => state.error);\n const errorCode = useAuthStore((state) => state.errorCode);\n const isAuthenticated = useAuthStore((state) => state.isAuthenticated);\n const user = useAuthStore((state) => state.user);\n const accessToken = useAuthStore((state) => state.accessToken);\n const disableCurrentUserQuery = options.disableCurrentUserQuery ?? false;\n\n // Track whether signIn just completed — skip the CurrentUser refetch\n // because signIn already returns the full user object.\n const loginJustCompletedRef = useRef(false);\n\n const context = useAuth();\n const eventBus = useEventBus();\n const apolloClient = useApolloClient();\n const [signInMutation] = useMutation<SignInResponse, SignInVariables>(\n SignInDocument,\n );\n const [signUpMutation] = useMutation<SignUpResponse, SignUpVariables>(\n SignUpDocument,\n );\n const [signOutMutation] = useMutation<SignOutResponse>(SignOutDocument);\n const [refreshTokenMutation] = useMutation<\n RefreshTokenResponse,\n RefreshTokenVariables\n >(RefreshTokenDocument);\n const [verifyEmailMutation] = useMutation<\n VerifyEmailResponse,\n VerifyEmailVariables\n >(VerifyEmailDocument);\n const [resendVerificationMutation] = useMutation<\n ResendVerificationEmailResponse,\n ResendVerificationEmailVariables\n >(ResendVerificationEmailDocument);\n\n /**\n * Sign in with email/username and password\n * Uses signIn mutation which returns correct token format (with 'sub' field)\n */\n const signIn = async (input: SignInInput): Promise<AuthResult> => {\n const store = useAuthStore.getState();\n\n try {\n store.setLoading(true);\n store.setError(null);\n\n // rememberMe is sent to the backend so the server can issue\n // longer-lived refresh tokens (30 days instead of 7 days)\n\n // Use signIn mutation (returns correct token format with 'sub' field)\n const result = await signInMutation({\n variables: { input },\n // Override client-level errorPolicy to ensure errors are thrown\n // and caught by our catch block for proper error message extraction\n errorPolicy: \"none\",\n });\n\n // If we reach here, the mutation succeeded (no errors thrown)\n // Check if data.signIn is present\n if (!result.data || !result.data.signIn) {\n const errorMessage =\n \"Sign in failed. Please check your credentials and try again.\";\n store.setError(errorMessage);\n return {\n success: false,\n error: errorMessage,\n } as AuthResult;\n }\n\n const data = result.data;\n\n const authResult = data.signIn;\n\n // Check if MFA is required\n if (authResult.requiresMFA && authResult.mfaChallengeId) {\n store.setMfaRequired(true, authResult.mfaChallengeId);\n\n // Return success but with requiresMFA flag\n return {\n success: true,\n requiresMFA: true,\n mfaChallengeId: authResult.mfaChallengeId,\n metadata: {\n mfaMethod: authResult.mfaMethod,\n },\n } as AuthResult;\n }\n\n // Normal login without MFA - store the authentication result\n if (\n authResult.user &&\n authResult.accessToken &&\n authResult.refreshToken\n ) {\n // Signal that user data is fresh from signIn — skip CurrentUser refetch.\n loginJustCompletedRef.current = true;\n store.setUser(authResult.user);\n store.setAuthTokens(authResult.accessToken, authResult.refreshToken);\n store.setAuthenticated(true);\n store.setMfaRequired(false);\n\n const expiresAt = authResult.expiresIn\n ? Date.now() + authResult.expiresIn * 1000\n : undefined;\n loginProfile(toProfileUser(authResult.user), {\n accessToken: authResult.accessToken,\n refreshToken: authResult.refreshToken,\n expiresAt,\n });\n setShellSsoCookie(authResult.user.id);\n\n eventBus.emit(\n \"auth.signed_in\",\n {\n userId: authResult.user.id,\n tenantId: authResult.user.tenantId,\n workspaceId: authResult.user.defaultWorkspaceId,\n mfaEnabled: authResult.user.mfaEnabled,\n emailVerified: authResult.user.emailVerified,\n },\n { component: \"useAuthOperations\" },\n );\n\n context.onAuthSuccess?.(\n authResult.user,\n authResult.accessToken,\n authResult.refreshToken,\n );\n\n // Convert AuthPayload to AuthResult for consistent return type\n return {\n success: true,\n user: authResult.user,\n token: authResult.accessToken,\n refreshToken: authResult.refreshToken,\n expiresIn: authResult.expiresIn,\n requiresMFA: false,\n } as AuthResult;\n } else {\n const errorMsg = \"Login failed: Invalid response from server\";\n store.setError(errorMsg);\n return {\n success: false,\n error: errorMsg,\n } as AuthResult;\n }\n } catch (err: unknown) {\n // Extract error message and code from GraphQL error responses.\n // The backend sends user-friendly messages in gqlError.message and\n // structured error codes in gqlError.extensions.code (e.g., ACCOUNT_DELETED,\n // ACCOUNT_SUSPENDED, ACCOUNT_INACTIVE, OAUTH_ONLY_ACCOUNT, EMAIL_NOT_VERIFIED,\n // UNAUTHENTICATED, INTERNAL_SERVER_ERROR).\n // IMPORTANT: Never use gqlError.extensions.originalError — it may contain\n // internal details that should not be shown to users.\n let message = \"Sign in failed\";\n let errorCode: string | null = null;\n const graphQLErrors = getGraphQLErrors(err);\n if (graphQLErrors.length > 0) {\n // Apollo v4 CombinedGraphQLErrors\n // Apollo v3 ApolloError — use message only, never originalError\n const gqlError = graphQLErrors[0];\n message = gqlError.message || message;\n errorCode = getGraphQLErrorCode(gqlError);\n } else {\n message = getUnknownErrorMessage(err) ?? message;\n }\n\n console.error(\n \"[SignIn] Error:\",\n message,\n errorCode ? `(${errorCode})` : \"\",\n );\n store.setError(message, errorCode);\n return {\n success: false,\n error: message,\n } as AuthResult;\n } finally {\n store.setLoading(false);\n }\n };\n\n /**\n * Sign up new user\n */\n const signUp = async (input: SignUpInput) => {\n const store = useAuthStore.getState();\n\n try {\n store.setLoading(true);\n store.setError(null);\n\n const mutationResult = await signUpMutation({\n variables: { input },\n });\n\n // With errorPolicy: 'all', errors are returned in result, not thrown\n // Check for GraphQL errors first\n const graphQLErrors = getGraphQLErrors(mutationResult);\n if (graphQLErrors.length > 0) {\n const errorMessage = graphQLErrors[0].message ?? \"Sign up failed\";\n console.error(\"[SignUp] GraphQL error:\", errorMessage);\n store.setError(errorMessage);\n throw new Error(errorMessage);\n }\n\n // Check if data.signUp is null (indicates GraphQL error without explicit error)\n if (!mutationResult.data || !mutationResult.data.signUp) {\n const errorMessage = \"Sign up failed: No response from server\";\n console.error(\"[SignUp] No data:\", errorMessage);\n store.setError(errorMessage);\n throw new Error(errorMessage);\n }\n\n const result = mutationResult.data.signUp;\n\n // Check if user was created\n if (!result.user) {\n throw new Error(\"Sign up failed: No user data returned\");\n }\n\n // Check if email verification is required (empty tokens)\n if (\n !result.accessToken ||\n !result.refreshToken ||\n result.accessToken === \"\" ||\n result.refreshToken === \"\"\n ) {\n // User created successfully but needs to verify email\n // Don't set authenticated state, but store the user for display\n store.setUser(result.user);\n store.setAuthenticated(false);\n\n // Return special result indicating email verification needed\n return {\n ...result,\n requiresEmailVerification: true,\n };\n }\n\n // Normal sign up with immediate access (tokens provided)\n store.setUser(result.user);\n store.setAuthTokens(result.accessToken, result.refreshToken);\n store.setAuthenticated(true);\n\n eventBus.emit(\n \"auth.signed_up\",\n {\n userId: result.user.id,\n tenantId: result.user.tenantId,\n workspaceId: result.user.defaultWorkspaceId,\n mfaEnabled: result.user.mfaEnabled,\n emailVerified: result.user.emailVerified,\n },\n { component: \"useAuthOperations\" },\n );\n\n context.onAuthSuccess?.(\n result.user,\n result.accessToken,\n result.refreshToken,\n );\n\n return result;\n } catch (err: unknown) {\n // Extract error message from Apollo error or regular error\n let message = \"Sign up failed. Please try again.\";\n let errorCode: string | undefined;\n\n const graphQLErrors = getGraphQLErrors(err);\n if (graphQLErrors.length > 0) {\n const gqlError = graphQLErrors[0];\n errorCode = getGraphQLErrorCode(gqlError) ?? undefined;\n const rawMessage = gqlError.message ?? \"\";\n\n // Map backend error codes to user-friendly messages\n message = mapSignUpError(rawMessage, errorCode);\n } else {\n const rawMessage = getUnknownErrorMessage(err);\n if (rawMessage) {\n message = mapSignUpError(rawMessage);\n }\n }\n\n store.setError(message, errorCode ?? null);\n throw new Error(message);\n } finally {\n store.setLoading(false);\n }\n };\n\n /**\n * Sign out current session\n */\n const signOut = async () => {\n const store = useAuthStore.getState();\n const signedOutUser = store.user;\n\n try {\n await signOutMutation();\n } catch (err) {\n console.error(\"Sign out failed:\", err);\n }\n\n // Always clear Apollo cache so stale user data doesn't survive re-login\n await apolloClient.clearStore();\n store.clearAuth();\n eventBus.emit(\n \"auth.signed_out\",\n {\n userId: signedOutUser?.id,\n tenantId: signedOutUser?.tenantId,\n workspaceId: signedOutUser?.defaultWorkspaceId,\n },\n { component: \"useAuthOperations\" },\n );\n\n // If other profiles exist after clearing the current one, switch to the first\n // remaining profile instead of fully logging out.\n const remainingProfiles = getProfiles().filter(\n (p) => p.id !== signedOutUser?.id,\n );\n if (remainingProfiles.length > 0) {\n const nextProfile = remainingProfiles[0];\n const nextTokens = getProfileTokens(nextProfile.id);\n const nextUser = getProfileUser(nextProfile.id);\n if (nextTokens?.accessToken && nextUser) {\n // Switch to next profile instead of fully logging out\n store.loadFromProfile(nextProfile.id);\n return;\n }\n }\n\n context.onLogout?.();\n };\n\n /**\n * Refresh access token\n */\n const refreshToken = async () => {\n const store = useAuthStore.getState();\n\n try {\n const token = store.refreshToken;\n if (!token) throw new Error(\"No refresh token available\");\n\n const { data } = await refreshTokenMutation({\n variables: { token },\n });\n\n const result = data!.refreshToken;\n\n // Refresh token should always return valid tokens and user\n if (!result.accessToken || !result.refreshToken || !result.user) {\n throw new Error(\"Token refresh failed: Invalid response from server\");\n }\n\n store.setAuthTokens(result.accessToken, result.refreshToken);\n store.setUser(result.user);\n\n eventBus.emit(\n \"auth.token_refreshed\",\n {\n userId: result.user.id,\n tenantId: result.user.tenantId,\n workspaceId: result.user.defaultWorkspaceId,\n mfaEnabled: result.user.mfaEnabled,\n emailVerified: result.user.emailVerified,\n },\n { component: \"useAuthOperations\" },\n );\n\n return result;\n } catch (err) {\n // Refresh failed, logout user\n eventBus.emit(\n \"auth.token_refresh_failed\",\n {\n userId: store.user?.id,\n tenantId: store.user?.tenantId,\n workspaceId: store.user?.defaultWorkspaceId,\n reason: err instanceof Error ? err.message : \"Token refresh failed\",\n },\n { component: \"useAuthOperations\" },\n );\n store.clearAuth();\n throw err;\n }\n };\n\n /**\n * Get current user.\n * Skip when:\n * - Not authenticated\n * - Login just completed (user data is already fresh from signIn response)\n *\n * The loginJustCompletedRef is cleared after the first successful fetch\n * or on the next render cycle after navigation.\n */\n const { data: currentUserData, refetch: refetchCurrentUser } =\n useQuery<CurrentUserResponse>(CurrentUserDocument, {\n skip:\n disableCurrentUserQuery ||\n !isAuthenticated ||\n loginJustCompletedRef.current,\n fetchPolicy: \"network-only\",\n });\n\n // Handle currentUser data updates (replaces onCompleted callback)\n useEffect(() => {\n if (currentUserData?.currentUser) {\n useAuthStore.getState().setUser(currentUserData.currentUser);\n }\n }, [currentUserData]);\n\n // Clear loginJustCompletedRef after navigation away from login page\n // so subsequent CurrentUser queries work normally (e.g., profile page).\n useEffect(() => {\n if (loginJustCompletedRef.current && isAuthenticated) {\n const timer = setTimeout(() => {\n loginJustCompletedRef.current = false;\n }, 2000);\n return () => clearTimeout(timer);\n }\n }, [isAuthenticated]);\n\n /**\n * Verify email with token\n * Returns { success, message, email } on success\n */\n const verifyEmail = async (\n token: string,\n ): Promise<{ success: boolean; message: string; email?: string }> => {\n const store = useAuthStore.getState();\n\n try {\n store.setLoading(true);\n const { data } = await verifyEmailMutation({ variables: { token } });\n\n if (data!.verifyEmail.success && store.user) {\n store.setUser({ ...store.user, emailVerified: true });\n }\n\n return data!.verifyEmail;\n } catch (err) {\n const message =\n err instanceof Error ? err.message : \"Email verification failed\";\n store.setError(message);\n throw err;\n } finally {\n store.setLoading(false);\n }\n };\n\n /**\n * Resend verification email\n */\n const resendVerification = async (email: string) => {\n try {\n const { data } = await resendVerificationMutation({\n variables: { email },\n });\n return data!.resendVerificationEmail;\n } catch (err) {\n console.error(\"Failed to resend verification email:\", err);\n throw err;\n }\n };\n\n // Legacy aliases for backwards compatibility\n const login = signIn;\n const register = signUp;\n const logout = signOut;\n\n return {\n // New API\n signIn,\n signUp,\n signOut,\n refreshToken,\n verifyEmail,\n resendVerification,\n refetchCurrentUser,\n // Legacy aliases\n login,\n register,\n logout,\n // State (reactive via selectors)\n isLoading,\n error,\n errorCode,\n isAuthenticated,\n user,\n accessToken,\n };\n}\n"],"mappings":"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"}
|
|
@@ -4,7 +4,7 @@ import { useCallback as n, useEffect as r, useRef as i, useState as a } from "re
|
|
|
4
4
|
import { getStoredAuthToken as o, graphqlFetch as s } from "@burdenoff/fe-libs/shared/graphql";
|
|
5
5
|
import { useActiveContext as c } from "@burdenoff/fe-libs/shared/providers/shell/ActiveContextProvider";
|
|
6
6
|
//#region src/auth/hooks/useContextSwitcherData.ts
|
|
7
|
-
var l = "\n query MyOrganizations {\n myOrganizations {\n total\n items {\n id\n name\n slug\n tenantId\n description\n logo\n website\n status\n type\n createdAt\n updatedAt\n }\n }\n }\n", u = "\n query
|
|
7
|
+
var l = "\n query MyOrganizations {\n myOrganizations {\n total\n items {\n id\n name\n slug\n tenantId\n description\n logo\n website\n status\n type\n createdAt\n updatedAt\n }\n }\n }\n", u = "\n query WorkspacesByOrg {\n workspaces: myWorkspaces(limit: 100) {\n total\n items {\n id\n name\n slug\n tenantId\n organizationId\n type\n status\n region\n createdAt\n updatedAt\n }\n }\n }\n", d = "\n query ContextSwitcherBootstrap {\n myOrganizations {\n total\n items {\n id\n name\n slug\n tenantId\n description\n logo\n website\n status\n type\n createdAt\n updatedAt\n }\n }\n workspaces: myWorkspaces(limit: 100) {\n total\n items {\n id\n name\n slug\n tenantId\n organizationId\n type\n status\n region\n createdAt\n updatedAt\n }\n }\n }\n", f = "\n mutation IssueWorkspaceToken($input: IssueWorkspaceTokenInput!) {\n issueWorkspaceToken(input: $input) {\n success\n token\n expiresAt\n organizationId\n workspaceId\n projectId\n scopes\n error {\n code\n message\n }\n }\n }\n", p = "\n query MyProjectsForContext {\n projects: myProjects {\n total\n items {\n id\n name\n key\n workspaceId\n status\n archived\n createdAt\n updatedAt\n }\n }\n }\n", m = "\n mutation CreateDefaultProject($input: CreateProjectInput!) {\n createProject(input: $input) {\n id\n name\n key\n workspaceId\n status\n archived\n createdAt\n updatedAt\n }\n }\n";
|
|
8
8
|
async function h({ authToken: e, workspaceBaseUrl: t, workspaceToken: n }) {
|
|
9
9
|
let r = await s({
|
|
10
10
|
gateway: "workspace",
|
|
@@ -93,6 +93,7 @@ function C(o) {
|
|
|
93
93
|
let r = B.current ?? t.currentOrganizationId, i = await s({
|
|
94
94
|
gateway: "global",
|
|
95
95
|
query: r ? d : l,
|
|
96
|
+
variables: void 0,
|
|
96
97
|
authToken: n,
|
|
97
98
|
baseUrl: H
|
|
98
99
|
});
|
|
@@ -131,12 +132,12 @@ function C(o) {
|
|
|
131
132
|
}), t.setWorkspaces([]), t.setProjects([]), t.setCurrentWorkspaceId(null), t.setCurrentProjectId(null), t.setWorkspaceToken(null)), f && f !== t.currentOrganizationId && t.setCurrentOrganizationId(f), c.length > 0 && !f && (t.setCurrentOrganizationId(c[0].id), f = c[0].id), f && e.getState().workspaces.length === 0) {
|
|
132
133
|
t.setLoadingWorkspaces(!0);
|
|
133
134
|
try {
|
|
134
|
-
let e = (r === f ? i.data?.workspaces?.items : void 0) || await s({
|
|
135
|
+
let e = (r === f ? i.data?.workspaces?.items?.filter((e) => e.organizationId === f) : void 0) || await s({
|
|
135
136
|
gateway: "global",
|
|
136
137
|
query: u,
|
|
137
138
|
authToken: n,
|
|
138
139
|
baseUrl: H
|
|
139
|
-
}).then((e) => e.errors?.length ? null : e.data?.workspaces?.items ?? []);
|
|
140
|
+
}).then((e) => e.errors?.length ? null : (e.data?.workspaces?.items ?? []).filter((e) => e.organizationId === f));
|
|
140
141
|
if (e) {
|
|
141
142
|
t.setWorkspaces(e);
|
|
142
143
|
let n = z.current ? e.find((e) => e.id === z.current) : null;
|
|
@@ -176,7 +177,7 @@ function C(o) {
|
|
|
176
177
|
authToken: i,
|
|
177
178
|
baseUrl: H
|
|
178
179
|
});
|
|
179
|
-
if (e.errors?.length) {
|
|
180
|
+
if (e.data?.workspaces?.items && (e.data.workspaces.items = e.data.workspaces.items.filter((e) => e.organizationId === t)), e.errors?.length) {
|
|
180
181
|
let t = e.errors[0]?.message ?? "GraphQL error";
|
|
181
182
|
throw b(e.errors) ? new y(t) : (console.error("Failed to fetch workspaces:", e.errors), Error(t));
|
|
182
183
|
}
|
|
@@ -342,7 +343,7 @@ function w(n) {
|
|
|
342
343
|
authToken: o,
|
|
343
344
|
baseUrl: d
|
|
344
345
|
}).then((e) => {
|
|
345
|
-
if (!e.errors?.length) {
|
|
346
|
+
if (e.data?.workspaces?.items && (e.data.workspaces.items = e.data.workspaces.items.filter((e) => e.organizationId === i)), !e.errors?.length) {
|
|
346
347
|
let n = (e.data?.workspaces?.items ?? []).filter((e) => e.organizationId === i);
|
|
347
348
|
t.setWorkspaces(n);
|
|
348
349
|
}
|