@burdenoff/microfe-devportal 2026.1101.1 → 2026.1102.1

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.
Files changed (41) hide show
  1. package/dist/devportal/DevportalRoutes.d.ts.map +1 -1
  2. package/dist/devportal/DevportalRoutes.js +13 -9
  3. package/dist/devportal/DevportalRoutes.js.map +1 -1
  4. package/dist/devportal/components/OnboardingChecklist.d.ts +16 -0
  5. package/dist/devportal/components/OnboardingChecklist.d.ts.map +1 -0
  6. package/dist/devportal/components/OnboardingChecklist.js +151 -0
  7. package/dist/devportal/components/OnboardingChecklist.js.map +1 -0
  8. package/dist/devportal/constants/developerLinks.d.ts +47 -0
  9. package/dist/devportal/constants/developerLinks.d.ts.map +1 -0
  10. package/dist/devportal/constants/developerLinks.js +27 -0
  11. package/dist/devportal/constants/developerLinks.js.map +1 -0
  12. package/dist/devportal/hooks/useGraphQL.d.ts.map +1 -1
  13. package/dist/devportal/hooks/useGraphQL.js +2 -1
  14. package/dist/devportal/hooks/useGraphQL.js.map +1 -1
  15. package/dist/devportal/pages/DashboardPage.d.ts.map +1 -1
  16. package/dist/devportal/pages/DashboardPage.js +107 -105
  17. package/dist/devportal/pages/DashboardPage.js.map +1 -1
  18. package/dist/devportal/pages/DocumentationPage.d.ts.map +1 -1
  19. package/dist/devportal/pages/DocumentationPage.js +114 -148
  20. package/dist/devportal/pages/DocumentationPage.js.map +1 -1
  21. package/dist/devportal/pages/PublisherProfilePage.d.ts.map +1 -1
  22. package/dist/devportal/pages/PublisherProfilePage.js +170 -160
  23. package/dist/devportal/pages/PublisherProfilePage.js.map +1 -1
  24. package/dist/devportal/pages/ResourcesPage.js +2 -2
  25. package/dist/devportal/pages/ResourcesPage.js.map +1 -1
  26. package/dist/devportal/pages/SetupPage.d.ts.map +1 -1
  27. package/dist/devportal/pages/SetupPage.js +363 -218
  28. package/dist/devportal/pages/SetupPage.js.map +1 -1
  29. package/dist/devportal/utils/developerOnboarding.d.ts +85 -0
  30. package/dist/devportal/utils/developerOnboarding.d.ts.map +1 -0
  31. package/dist/devportal/utils/developerOnboarding.js +104 -0
  32. package/dist/devportal/utils/developerOnboarding.js.map +1 -0
  33. package/dist/generated/global-operations.d.ts +53 -0
  34. package/dist/generated/global-operations.d.ts.map +1 -1
  35. package/dist/generated/global-operations.js +170 -134
  36. package/dist/generated/global-operations.js.map +1 -1
  37. package/dist/translations/en.d.ts +50 -0
  38. package/dist/translations/en.d.ts.map +1 -1
  39. package/dist/translations/en.js +51 -1
  40. package/dist/translations/en.js.map +1 -1
  41. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"useGraphQL.js","names":[],"sources":["../../../src/devportal/hooks/useGraphQL.ts"],"sourcesContent":["/**\n * Apollo Client hooks and utilities for DevPortal\n *\n * This file provides the GraphQL client setup and custom hooks for the Developer Portal.\n * It wraps the generated Apollo hooks with additional functionality like error handling,\n * loading states, and authentication context.\n */\n\nimport {\n ApolloClient,\n InMemoryCache,\n createHttpLink,\n from,\n split,\n} from \"@apollo/client/core\";\nimport { CombinedGraphQLErrors, ServerError } from \"@apollo/client/errors\";\nimport { setContext } from \"@apollo/client/link/context\";\nimport { onError } from \"@apollo/client/link/error\";\nimport { GraphQLWsLink } from \"@apollo/client/link/subscriptions\";\nimport { getMainDefinition } from \"@apollo/client/utilities\";\nimport { createClient as createWsClient } from \"graphql-ws\";\nimport {\n isAuthNetworkError,\n redirectToLogin,\n} from \"@burdenoff/fe-libs/shared/utils\";\n\nfunction hasSessionAuthErrors(\n errors: ReadonlyArray<{\n message?: string;\n extensions?: Record<string, unknown>;\n }> = [],\n): boolean {\n return errors.some((error) => {\n const code =\n typeof error.extensions?.code === \"string\" ? error.extensions.code : \"\";\n const errorCode =\n typeof error.extensions?.errorCode === \"string\"\n ? error.extensions.errorCode\n : \"\";\n const message = (error.message ?? \"\").toLowerCase();\n\n return (\n code === \"UNAUTHENTICATED\" ||\n errorCode === \"TOKEN_EXPIRED\" ||\n message.includes(\"token_expired\") ||\n message.includes(\"token expired\") ||\n message.includes(\"session expired\") ||\n message.includes(\"unauthorized\") ||\n message.includes(\"unauthenticated\") ||\n message.includes(\"authentication required\") ||\n message.includes(\"workspace context missing\")\n );\n });\n}\n\n// Re-export all generated hooks for direct use\nexport * from \"../../generated/global-operations\";\n\n// Development mode check\nconst isDevelopment =\n typeof import.meta !== \"undefined\" && import.meta.env?.DEV === true;\n\n// Default GraphQL endpoints - can be overridden via environment.\n// In standalone dev mode we route through same-origin Vite proxies to avoid CORS.\nconst DEFAULT_GRAPHQL_ENDPOINT =\n (typeof import.meta !== \"undefined\" &&\n import.meta.env?.VITE_GRAPHQL_ENDPOINT) ||\n (isDevelopment ? \"/devportal-graphql\" : \"http://localhost:4015/graphql\");\n\nconst DEFAULT_AUTH_GRAPHQL_ENDPOINT =\n (typeof import.meta !== \"undefined\" &&\n import.meta.env?.VITE_AUTH_GRAPHQL_ENDPOINT) ||\n (isDevelopment ? \"/auth-graphql\" : \"http://localhost:4011/graphql\");\n\nconst AUTH_OPERATION_NAMES = new Set([\n \"CurrentUserApps\",\n \"GetAuthApp\",\n \"AppsActingAsCurrentUser\",\n \"CreateAuthApp\",\n \"UpdateAuthApp\",\n \"DeactivateApp\",\n \"RotateAppSecret\",\n \"AuthenticateApp\",\n \"GenerateAppToken\",\n \"GenerateAppAsUserToken\",\n \"ExchangeToken\",\n \"CurrentUserDelegations\",\n \"CurrentUserGrantedDelegations\",\n \"GrantDelegation\",\n \"RevokeDelegation\",\n \"MyApiKeys\",\n \"GenerateAPIKey\",\n \"RevokeAPIKeyAuth\",\n \"RotateAPIKeyAuth\",\n \"AuthenticateAPIKey\",\n]);\n\n// Development actor ID - simulates gateway-injected headers\n// In production, the gateway adds these after JWT verification\nconst DEV_ACTOR_ID =\n (typeof import.meta !== \"undefined\" && import.meta.env?.VITE_DEV_ACTOR_ID) ||\n \"dev-user-001\";\n\nconst DEV_ACTOR_TYPE =\n (typeof import.meta !== \"undefined\" &&\n import.meta.env?.VITE_DEV_ACTOR_TYPE) ||\n \"user\";\n\nfunction toWsUrl(httpUrl: string): string {\n return httpUrl.replace(/^https/, \"wss\").replace(/^http/, \"ws\");\n}\n\n// Create the HTTP links\nconst devportalHttpLink = createHttpLink({\n uri: DEFAULT_GRAPHQL_ENDPOINT,\n});\n\nconst authHttpLink = createHttpLink({\n uri: DEFAULT_AUTH_GRAPHQL_ENDPOINT,\n});\n\nconst httpLink = split(\n ({ operationName }) => {\n if (!operationName) {\n return false;\n }\n\n return AUTH_OPERATION_NAMES.has(operationName);\n },\n authHttpLink,\n devportalHttpLink,\n);\n\n// Auth link to add authentication headers\nconst authLink = setContext((_, { headers }) => {\n // Get auth token from storage or context\n const token =\n typeof window !== \"undefined\" ? localStorage.getItem(\"auth_token\") : null;\n\n // Build headers\n const authHeaders: Record<string, string> = {\n ...headers,\n };\n\n if (token) {\n authHeaders[\"authorization\"] = `Bearer ${token}`;\n }\n\n // In development mode, add actor headers to simulate gateway behavior\n // In production, the API Gateway adds these headers after JWT verification\n if (isDevelopment) {\n // Use actor ID from localStorage (set by dev tools) or fall back to env/default\n const actorId =\n (typeof window !== \"undefined\" && localStorage.getItem(\"dev_actor_id\")) ||\n DEV_ACTOR_ID;\n const actorType =\n (typeof window !== \"undefined\" &&\n localStorage.getItem(\"dev_actor_type\")) ||\n DEV_ACTOR_TYPE;\n\n authHeaders[\"x-actor-id\"] = actorId;\n authHeaders[\"x-actor-type\"] = actorType;\n }\n\n return { headers: authHeaders };\n});\n\nconst errorLink = onError(({ error }) => {\n if (!error) return;\n\n const graphQLErrors =\n \"errors\" in error && Array.isArray(error.errors) ? error.errors : [];\n\n if (hasSessionAuthErrors(graphQLErrors)) {\n redirectToLogin();\n return;\n }\n\n if (error instanceof Error && isAuthNetworkError(error)) {\n redirectToLogin();\n }\n});\n\nconst wsLink = new GraphQLWsLink(\n createWsClient({\n url: toWsUrl(DEFAULT_GRAPHQL_ENDPOINT),\n connectionParams: () => {\n // Grab the auth token from the auth link's context builder for WS handshake\n return { authorization: \"\" }; // token injected server-side from cookie/session\n },\n }),\n);\n\nconst splitLink = split(\n ({ query }) => {\n const definition = getMainDefinition(query);\n return (\n definition.kind === \"OperationDefinition\" &&\n definition.operation === \"subscription\"\n );\n },\n wsLink,\n from([errorLink, authLink, httpLink]),\n);\n\n// Create the Apollo Client instance\n// NOTE: defaultOptions.errorPolicy is intentionally omitted. Apollo Client v4\n// guards it behind a DeclareDefaultOptions module augmentation that does not\n// resolve reliably in CI's frozen install, so it is left at the v4 default\n// (\"none\"). Per-operation errorPolicy: \"all\" can be set on individual hooks\n// where partial-data-on-error handling is required.\nexport const apolloClient = new ApolloClient({\n link: splitLink,\n cache: new InMemoryCache({\n typePolicies: {\n Query: {\n fields: {\n // Pagination policies for connection types\n developerOrganizations: {\n keyArgs: [\"tier\", \"isVerified\", \"isCertified\", \"partnerTier\"],\n merge(existing, incoming, { args }) {\n if (!args?.offset || args.offset === 0) {\n return incoming;\n }\n return {\n ...incoming,\n edges: [...(existing?.edges || []), ...incoming.edges],\n };\n },\n },\n applications: {\n keyArgs: [\"status\", \"visibility\", \"developerId\"],\n merge(existing, incoming, { args }) {\n if (!args?.offset || args.offset === 0) {\n return incoming;\n }\n return {\n ...incoming,\n edges: [...(existing?.edges || []), ...incoming.edges],\n };\n },\n },\n developerOrgSales: {\n keyArgs: [\n \"organizationId\",\n \"applicationId\",\n \"status\",\n \"pricingModel\",\n \"isPaidOut\",\n ],\n merge(existing, incoming, { args }) {\n if (!args?.offset || args.offset === 0) {\n return incoming;\n }\n return {\n ...incoming,\n edges: [...(existing?.edges || []), ...incoming.edges],\n };\n },\n },\n developerOrgPayouts: {\n keyArgs: [\"organizationId\", \"status\"],\n merge(existing, incoming, { args }) {\n if (!args?.offset || args.offset === 0) {\n return incoming;\n }\n return {\n ...incoming,\n edges: [...(existing?.edges || []), ...incoming.edges],\n };\n },\n },\n developerOrgMembers: {\n keyArgs: [\"organizationId\", \"role\"],\n merge(existing, incoming, { args }) {\n if (!args?.offset || args.offset === 0) return incoming;\n return {\n ...incoming,\n edges: [...(existing?.edges || []), ...incoming.edges],\n };\n },\n },\n developerOrgInvitations: {\n keyArgs: [\"organizationId\", \"status\"],\n merge(existing, incoming, { args }) {\n if (!args?.offset || args.offset === 0) return incoming;\n return {\n ...incoming,\n edges: [...(existing?.edges || []), ...incoming.edges],\n };\n },\n },\n webhooks: {\n keyArgs: [\"applicationId\", \"status\"],\n merge(existing, incoming, { args }) {\n if (!args?.offset || args.offset === 0) return incoming;\n return {\n ...incoming,\n edges: [...(existing?.edges || []), ...incoming.edges],\n };\n },\n },\n webhookDeliveries: {\n keyArgs: [\"webhookId\", \"status\"],\n merge(existing, incoming, { args }) {\n if (!args?.offset || args.offset === 0) return incoming;\n return {\n ...incoming,\n edges: [...(existing?.edges || []), ...incoming.edges],\n };\n },\n },\n auditLogs: {\n keyArgs: [\"resource\", \"resourceId\", \"actorId\"],\n merge(existing, incoming, { args }) {\n if (!args?.offset || args.offset === 0) return incoming;\n return {\n ...incoming,\n edges: [...(existing?.edges || []), ...incoming.edges],\n };\n },\n },\n applicationVersions: {\n // TODO(phase1-backend): Switch to ApplicationVersionConnection cursor pagination\n // once backend ships connection type. See stability tracker.\n keyArgs: [\"applicationId\"],\n merge(existing, incoming, { args }) {\n if (!args?.offset || args.offset === 0) return incoming;\n if (Array.isArray(incoming) && Array.isArray(existing)) {\n return [...existing, ...incoming];\n }\n return incoming;\n },\n },\n },\n },\n WebhookDelivery: {\n keyFields: [\"id\"],\n },\n ApplicationVersion: {\n keyFields: [\"id\"],\n },\n DeveloperOrganization: {\n keyFields: [\"id\"],\n },\n Application: {\n keyFields: [\"id\"],\n },\n DeveloperOrgMember: {\n keyFields: [\"id\"],\n },\n DeveloperOrgInvitation: {\n keyFields: [\"id\"],\n },\n Webhook: {\n keyFields: [\"id\"],\n },\n ApiKey: {\n keyFields: [\"id\"],\n },\n OAuthClient: {\n keyFields: [\"id\"],\n },\n },\n }),\n defaultOptions: {\n watchQuery: {\n fetchPolicy: \"cache-and-network\",\n },\n query: {\n fetchPolicy: \"cache-first\",\n },\n },\n});\n\n/**\n * Error handling utilities\n */\nexport interface GraphQLErrorInfo {\n message: string;\n code?: string;\n path?: readonly (string | number)[];\n}\n\n// Type for Apollo errors (can be CombinedGraphQLErrors, ServerError, or Error)\nexport type ApolloErrorLike = Error & {\n graphQLErrors?: readonly {\n message: string;\n extensions?: { code?: string };\n path?: readonly (string | number)[];\n }[];\n networkError?: Error | null;\n};\n\nexport function parseGraphQLError(error: ApolloErrorLike): GraphQLErrorInfo[] {\n if (error instanceof CombinedGraphQLErrors) {\n return error.errors.map((err) => ({\n message: err.message,\n code: err.extensions?.code as string | undefined,\n path: err.path,\n }));\n }\n\n if (error.graphQLErrors && error.graphQLErrors.length > 0) {\n return error.graphQLErrors.map((err) => ({\n message: err.message,\n code: err.extensions?.code as string | undefined,\n path: err.path,\n }));\n }\n\n if (error instanceof ServerError || error.networkError) {\n return [\n {\n message: \"Network error. Please check your connection and try again.\",\n code: \"NETWORK_ERROR\",\n },\n ];\n }\n\n return [\n {\n message: error.message || \"An unexpected error occurred\",\n code: \"UNKNOWN_ERROR\",\n },\n ];\n}\n\n/**\n * Get a user-friendly error message from an Apollo error\n */\nexport function getErrorMessage(error: ApolloErrorLike): string {\n const errors = parseGraphQLError(error);\n return errors.map((e) => e.message).join(\", \");\n}\n\n/**\n * Check if an error is an authentication error\n */\nexport function isAuthError(error: ApolloErrorLike): boolean {\n const errors = parseGraphQLError(error);\n return errors.some(\n (e) => e.code === \"UNAUTHENTICATED\" || e.code === \"FORBIDDEN\",\n );\n}\n\n/**\n * Check if an error is a validation error\n */\nexport function isValidationError(error: ApolloErrorLike): boolean {\n const errors = parseGraphQLError(error);\n return errors.some(\n (e) => e.code === \"BAD_USER_INPUT\" || e.code === \"VALIDATION_ERROR\",\n );\n}\n\n/**\n * Clear Apollo cache (useful for logout)\n */\nexport async function clearApolloCache(): Promise<void> {\n await apolloClient.clearStore();\n}\n\n/**\n * Reset Apollo store and refetch queries\n */\nexport async function resetApolloStore(): Promise<void> {\n await apolloClient.resetStore();\n}\n"],"mappings":";;;;;;;;;;;AA0BA,SAAS,EACP,IAGK,EAAE,EACE;AACT,QAAO,EAAO,MAAM,MAAU;EAC5B,IAAM,IACJ,OAAO,EAAM,YAAY,QAAS,WAAW,EAAM,WAAW,OAAO,IACjE,IACJ,OAAO,EAAM,YAAY,aAAc,WACnC,EAAM,WAAW,YACjB,IACA,KAAW,EAAM,WAAW,IAAI,aAAa;AAEnD,SACE,MAAS,qBACT,MAAc,mBACd,EAAQ,SAAS,gBAAgB,IACjC,EAAQ,SAAS,gBAAgB,IACjC,EAAQ,SAAS,kBAAkB,IACnC,EAAQ,SAAS,eAAe,IAChC,EAAQ,SAAS,kBAAkB,IACnC,EAAQ,SAAS,0BAA0B,IAC3C,EAAQ,SAAS,4BAA4B;GAE/C;;AAOJ,IAAM,IACkC,IAIlC,IAGH,IAAgB,uBAAuB,iCAEpC,IAGH,IAAgB,kBAAkB,iCAE/B,IAAuB,IAAI,IAAI;CACnC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC,EAII,IAEJ,gBAEI,IAGJ;AAEF,SAAS,EAAQ,GAAyB;AACxC,QAAO,EAAQ,QAAQ,UAAU,MAAM,CAAC,QAAQ,SAAS,KAAK;;AAIhE,IAAM,IAAoB,EAAe,EACvC,KAAK,GACN,CAAC,EAMI,IAAW,GACd,EAAE,uBACI,IAIE,EAAqB,IAAI,EAAc,GAHrC,IAPQ,EAAe,EAClC,KAAK,GACN,CAAC,EAWA,EACD,EAGK,IAAW,GAAY,GAAG,EAAE,iBAAc;CAE9C,IAAM,IACJ,OAAO,SAAW,MAAc,aAAa,QAAQ,aAAa,GAAG,MAGjE,IAAsC,EAC1C,GAAG,GACJ;AAQD,KANI,MACF,EAAY,gBAAmB,UAAU,MAKvC,GAAe;EAEjB,IAAM,IACH,OAAO,SAAW,OAAe,aAAa,QAAQ,eAAe,IACtE,GACI,IACH,OAAO,SAAW,OACjB,aAAa,QAAQ,iBAAiB,IACxC;AAGF,EADA,EAAY,gBAAgB,GAC5B,EAAY,kBAAkB;;AAGhC,QAAO,EAAE,SAAS,GAAa;EAC/B,EAEI,IAAY,GAAS,EAAE,eAAY;AAClC,QAKL;MAAI,EAFF,YAAY,KAAS,MAAM,QAAQ,EAAM,OAAO,GAAG,EAAM,SAAS,EAAE,CAE/B,EAAE;AACvC,MAAiB;AACjB;;AAGF,EAAI,aAAiB,SAAS,EAAmB,EAAM,IACrD,GAAiB;;EAEnB,EA8BW,IAAe,IAAI,EAAa;CAC3C,MAnBgB,GACf,EAAE,eAAY;EACb,IAAM,IAAa,EAAkB,EAAM;AAC3C,SACE,EAAW,SAAS,yBACpB,EAAW,cAAc;IAfhB,IAAI,EACjB,EAAe;EACb,KAAK,EAAQ,EAAyB;EACtC,yBAES,EAAE,eAAe,IAAI;EAE/B,CAAC,CACH,EAWC,EAAK;EAAC;EAAW;EAAU;EAAS,CAAC,CACtC;CAUC,OAAO,IAAI,EAAc,EACvB,cAAc;EACZ,OAAO,EACL,QAAQ;GAEN,wBAAwB;IACtB,SAAS;KAAC;KAAQ;KAAc;KAAe;KAAc;IAC7D,MAAM,GAAU,GAAU,EAAE,WAAQ;AAIlC,YAHI,CAAC,GAAM,UAAU,EAAK,WAAW,IAC5B,IAEF;MACL,GAAG;MACH,OAAO,CAAC,GAAI,GAAU,SAAS,EAAE,EAAG,GAAG,EAAS,MAAM;MACvD;;IAEJ;GACD,cAAc;IACZ,SAAS;KAAC;KAAU;KAAc;KAAc;IAChD,MAAM,GAAU,GAAU,EAAE,WAAQ;AAIlC,YAHI,CAAC,GAAM,UAAU,EAAK,WAAW,IAC5B,IAEF;MACL,GAAG;MACH,OAAO,CAAC,GAAI,GAAU,SAAS,EAAE,EAAG,GAAG,EAAS,MAAM;MACvD;;IAEJ;GACD,mBAAmB;IACjB,SAAS;KACP;KACA;KACA;KACA;KACA;KACD;IACD,MAAM,GAAU,GAAU,EAAE,WAAQ;AAIlC,YAHI,CAAC,GAAM,UAAU,EAAK,WAAW,IAC5B,IAEF;MACL,GAAG;MACH,OAAO,CAAC,GAAI,GAAU,SAAS,EAAE,EAAG,GAAG,EAAS,MAAM;MACvD;;IAEJ;GACD,qBAAqB;IACnB,SAAS,CAAC,kBAAkB,SAAS;IACrC,MAAM,GAAU,GAAU,EAAE,WAAQ;AAIlC,YAHI,CAAC,GAAM,UAAU,EAAK,WAAW,IAC5B,IAEF;MACL,GAAG;MACH,OAAO,CAAC,GAAI,GAAU,SAAS,EAAE,EAAG,GAAG,EAAS,MAAM;MACvD;;IAEJ;GACD,qBAAqB;IACnB,SAAS,CAAC,kBAAkB,OAAO;IACnC,MAAM,GAAU,GAAU,EAAE,WAAQ;AAElC,YADI,CAAC,GAAM,UAAU,EAAK,WAAW,IAAU,IACxC;MACL,GAAG;MACH,OAAO,CAAC,GAAI,GAAU,SAAS,EAAE,EAAG,GAAG,EAAS,MAAM;MACvD;;IAEJ;GACD,yBAAyB;IACvB,SAAS,CAAC,kBAAkB,SAAS;IACrC,MAAM,GAAU,GAAU,EAAE,WAAQ;AAElC,YADI,CAAC,GAAM,UAAU,EAAK,WAAW,IAAU,IACxC;MACL,GAAG;MACH,OAAO,CAAC,GAAI,GAAU,SAAS,EAAE,EAAG,GAAG,EAAS,MAAM;MACvD;;IAEJ;GACD,UAAU;IACR,SAAS,CAAC,iBAAiB,SAAS;IACpC,MAAM,GAAU,GAAU,EAAE,WAAQ;AAElC,YADI,CAAC,GAAM,UAAU,EAAK,WAAW,IAAU,IACxC;MACL,GAAG;MACH,OAAO,CAAC,GAAI,GAAU,SAAS,EAAE,EAAG,GAAG,EAAS,MAAM;MACvD;;IAEJ;GACD,mBAAmB;IACjB,SAAS,CAAC,aAAa,SAAS;IAChC,MAAM,GAAU,GAAU,EAAE,WAAQ;AAElC,YADI,CAAC,GAAM,UAAU,EAAK,WAAW,IAAU,IACxC;MACL,GAAG;MACH,OAAO,CAAC,GAAI,GAAU,SAAS,EAAE,EAAG,GAAG,EAAS,MAAM;MACvD;;IAEJ;GACD,WAAW;IACT,SAAS;KAAC;KAAY;KAAc;KAAU;IAC9C,MAAM,GAAU,GAAU,EAAE,WAAQ;AAElC,YADI,CAAC,GAAM,UAAU,EAAK,WAAW,IAAU,IACxC;MACL,GAAG;MACH,OAAO,CAAC,GAAI,GAAU,SAAS,EAAE,EAAG,GAAG,EAAS,MAAM;MACvD;;IAEJ;GACD,qBAAqB;IAGnB,SAAS,CAAC,gBAAgB;IAC1B,MAAM,GAAU,GAAU,EAAE,WAAQ;AAKlC,YAJI,CAAC,GAAM,UAAU,EAAK,WAAW,IAAU,IAC3C,MAAM,QAAQ,EAAS,IAAI,MAAM,QAAQ,EAAS,GAC7C,CAAC,GAAG,GAAU,GAAG,EAAS,GAE5B;;IAEV;GACF,EACF;EACD,iBAAiB,EACf,WAAW,CAAC,KAAK,EAClB;EACD,oBAAoB,EAClB,WAAW,CAAC,KAAK,EAClB;EACD,uBAAuB,EACrB,WAAW,CAAC,KAAK,EAClB;EACD,aAAa,EACX,WAAW,CAAC,KAAK,EAClB;EACD,oBAAoB,EAClB,WAAW,CAAC,KAAK,EAClB;EACD,wBAAwB,EACtB,WAAW,CAAC,KAAK,EAClB;EACD,SAAS,EACP,WAAW,CAAC,KAAK,EAClB;EACD,QAAQ,EACN,WAAW,CAAC,KAAK,EAClB;EACD,aAAa,EACX,WAAW,CAAC,KAAK,EAClB;EACF,EACF,CAAC;CACF,gBAAgB;EACd,YAAY,EACV,aAAa,qBACd;EACD,OAAO,EACL,aAAa,eACd;EACF;CACF,CAAC;AAqBF,SAAgB,EAAkB,GAA4C;AA0B5E,QAzBI,aAAiB,IACZ,EAAM,OAAO,KAAK,OAAS;EAChC,SAAS,EAAI;EACb,MAAM,EAAI,YAAY;EACtB,MAAM,EAAI;EACX,EAAE,GAGD,EAAM,iBAAiB,EAAM,cAAc,SAAS,IAC/C,EAAM,cAAc,KAAK,OAAS;EACvC,SAAS,EAAI;EACb,MAAM,EAAI,YAAY;EACtB,MAAM,EAAI;EACX,EAAE,GAGD,aAAiB,KAAe,EAAM,eACjC,CACL;EACE,SAAS;EACT,MAAM;EACP,CACF,GAGI,CACL;EACE,SAAS,EAAM,WAAW;EAC1B,MAAM;EACP,CACF;;AAMH,SAAgB,EAAgB,GAAgC;AAE9D,QADe,EAAkB,EAAM,CACzB,KAAK,MAAM,EAAE,QAAQ,CAAC,KAAK,KAAK;;AAMhD,SAAgB,EAAY,GAAiC;AAE3D,QADe,EAAkB,EAAM,CACzB,MACX,MAAM,EAAE,SAAS,qBAAqB,EAAE,SAAS,YACnD;;AAMH,SAAgB,EAAkB,GAAiC;AAEjE,QADe,EAAkB,EAAM,CACzB,MACX,MAAM,EAAE,SAAS,oBAAoB,EAAE,SAAS,mBAClD;;AAMH,eAAsB,IAAkC;AACtD,OAAM,EAAa,YAAY;;AAMjC,eAAsB,IAAkC;AACtD,OAAM,EAAa,YAAY"}
1
+ {"version":3,"file":"useGraphQL.js","names":[],"sources":["../../../src/devportal/hooks/useGraphQL.ts"],"sourcesContent":["/**\n * Apollo Client hooks and utilities for DevPortal\n *\n * This file provides the GraphQL client setup and custom hooks for the Developer Portal.\n * It wraps the generated Apollo hooks with additional functionality like error handling,\n * loading states, and authentication context.\n */\n\nimport {\n ApolloClient,\n InMemoryCache,\n createHttpLink,\n from,\n split,\n} from \"@apollo/client/core\";\nimport { CombinedGraphQLErrors, ServerError } from \"@apollo/client/errors\";\nimport { setContext } from \"@apollo/client/link/context\";\nimport { onError } from \"@apollo/client/link/error\";\nimport { GraphQLWsLink } from \"@apollo/client/link/subscriptions\";\nimport { getMainDefinition } from \"@apollo/client/utilities\";\nimport { createClient as createWsClient } from \"graphql-ws\";\nimport {\n isAuthNetworkError,\n redirectToLogin,\n} from \"@burdenoff/fe-libs/shared/utils\";\n\nfunction hasSessionAuthErrors(\n errors: ReadonlyArray<{\n message?: string;\n extensions?: Record<string, unknown>;\n }> = [],\n): boolean {\n return errors.some((error) => {\n const code =\n typeof error.extensions?.code === \"string\" ? error.extensions.code : \"\";\n const errorCode =\n typeof error.extensions?.errorCode === \"string\"\n ? error.extensions.errorCode\n : \"\";\n const message = (error.message ?? \"\").toLowerCase();\n\n return (\n code === \"UNAUTHENTICATED\" ||\n errorCode === \"TOKEN_EXPIRED\" ||\n message.includes(\"token_expired\") ||\n message.includes(\"token expired\") ||\n message.includes(\"session expired\") ||\n message.includes(\"unauthorized\") ||\n message.includes(\"unauthenticated\") ||\n message.includes(\"authentication required\") ||\n message.includes(\"workspace context missing\")\n );\n });\n}\n\n// Re-export all generated hooks for direct use\nexport * from \"../../generated/global-operations\";\n\n// Development mode check\nconst isDevelopment =\n typeof import.meta !== \"undefined\" && import.meta.env?.DEV === true;\n\n// Default GraphQL endpoints - can be overridden via environment.\n// In standalone dev mode we route through same-origin Vite proxies to avoid CORS.\nconst DEFAULT_GRAPHQL_ENDPOINT =\n (typeof import.meta !== \"undefined\" &&\n import.meta.env?.VITE_GRAPHQL_ENDPOINT) ||\n (isDevelopment ? \"/devportal-graphql\" : \"http://localhost:4015/graphql\");\n\nconst DEFAULT_AUTH_GRAPHQL_ENDPOINT =\n (typeof import.meta !== \"undefined\" &&\n import.meta.env?.VITE_AUTH_GRAPHQL_ENDPOINT) ||\n (isDevelopment ? \"/auth-graphql\" : \"http://localhost:4011/graphql\");\n\nconst AUTH_OPERATION_NAMES = new Set([\n \"CurrentUserApps\",\n \"GetAuthApp\",\n \"AppsActingAsCurrentUser\",\n \"CreateAuthApp\",\n \"UpdateAuthApp\",\n \"DeactivateApp\",\n \"RotateAppSecret\",\n \"AuthenticateApp\",\n \"GenerateAppToken\",\n \"GenerateAppAsUserToken\",\n \"ExchangeToken\",\n \"CurrentUserDelegations\",\n \"CurrentUserGrantedDelegations\",\n \"GrantDelegation\",\n \"RevokeDelegation\",\n \"MyApiKeys\",\n \"GenerateAPIKey\",\n \"RevokeAPIKeyAuth\",\n \"RotateAPIKeyAuth\",\n \"AuthenticateAPIKey\",\n]);\n\n// Development actor ID - simulates gateway-injected headers\n// In production, the gateway adds these after JWT verification\nconst DEV_ACTOR_ID =\n (typeof import.meta !== \"undefined\" && import.meta.env?.VITE_DEV_ACTOR_ID) ||\n \"dev-user-001\";\n\nconst DEV_ACTOR_TYPE =\n (typeof import.meta !== \"undefined\" &&\n import.meta.env?.VITE_DEV_ACTOR_TYPE) ||\n \"user\";\n\nfunction toWsUrl(httpUrl: string): string {\n return httpUrl.replace(/^https/, \"wss\").replace(/^http/, \"ws\");\n}\n\n// Create the HTTP links\nconst devportalHttpLink = createHttpLink({\n uri: DEFAULT_GRAPHQL_ENDPOINT,\n});\n\nconst authHttpLink = createHttpLink({\n uri: DEFAULT_AUTH_GRAPHQL_ENDPOINT,\n});\n\nconst httpLink = split(\n ({ operationName }) => {\n if (!operationName) {\n return false;\n }\n\n return AUTH_OPERATION_NAMES.has(operationName);\n },\n authHttpLink,\n devportalHttpLink,\n);\n\n// Auth link to add authentication headers\nconst authLink = setContext((_, { headers }) => {\n // Get auth token from storage or context\n const token =\n typeof window !== \"undefined\" ? localStorage.getItem(\"auth_token\") : null;\n\n // Build headers\n const authHeaders: Record<string, string> = {\n ...headers,\n };\n\n if (token) {\n authHeaders[\"authorization\"] = `Bearer ${token}`;\n }\n\n // In development mode, add actor headers to simulate gateway behavior\n // In production, the API Gateway adds these headers after JWT verification\n if (isDevelopment) {\n // Use actor ID from localStorage (set by dev tools) or fall back to env/default\n const actorId =\n (typeof window !== \"undefined\" && localStorage.getItem(\"dev_actor_id\")) ||\n DEV_ACTOR_ID;\n const actorType =\n (typeof window !== \"undefined\" &&\n localStorage.getItem(\"dev_actor_type\")) ||\n DEV_ACTOR_TYPE;\n\n authHeaders[\"x-actor-id\"] = actorId;\n authHeaders[\"x-actor-type\"] = actorType;\n }\n\n return { headers: authHeaders };\n});\n\nconst errorLink = onError(({ error }) => {\n if (!error) return;\n\n const graphQLErrors =\n \"errors\" in error && Array.isArray(error.errors) ? error.errors : [];\n\n if (hasSessionAuthErrors(graphQLErrors)) {\n redirectToLogin();\n return;\n }\n\n if (error instanceof Error && isAuthNetworkError(error)) {\n redirectToLogin();\n }\n});\n\nconst wsLink = new GraphQLWsLink(\n createWsClient({\n url: toWsUrl(DEFAULT_GRAPHQL_ENDPOINT),\n connectionParams: () => {\n // Grab the auth token from the auth link's context builder for WS handshake\n return { authorization: \"\" }; // token injected server-side from cookie/session\n },\n }),\n);\n\nconst splitLink = split(\n ({ query }) => {\n const definition = getMainDefinition(query);\n return (\n definition.kind === \"OperationDefinition\" &&\n definition.operation === \"subscription\"\n );\n },\n wsLink,\n from([errorLink, authLink, httpLink]),\n);\n\n// Create the Apollo Client instance\n// NOTE: defaultOptions.errorPolicy is intentionally omitted. Apollo Client v4\n// guards it behind a DeclareDefaultOptions module augmentation that does not\n// resolve reliably in CI's frozen install, so it is left at the v4 default\n// (\"none\"). Per-operation errorPolicy: \"all\" can be set on individual hooks\n// where partial-data-on-error handling is required.\nexport const apolloClient = new ApolloClient({\n link: splitLink,\n cache: new InMemoryCache({\n typePolicies: {\n Query: {\n fields: {\n // Pagination policies for connection types\n developerOrganizations: {\n keyArgs: [\"tier\", \"isVerified\", \"isCertified\", \"partnerTier\"],\n merge(existing, incoming, { args }) {\n if (!args?.offset || args.offset === 0) {\n return incoming;\n }\n return {\n ...incoming,\n edges: [...(existing?.edges || []), ...incoming.edges],\n };\n },\n },\n applications: {\n // organizationId is part of the key: one organization's app list\n // must never replace another's (or the unfiltered list) in the\n // cache (BOFF-10636).\n keyArgs: [\"status\", \"visibility\", \"developerId\", \"organizationId\"],\n merge(existing, incoming, { args }) {\n if (!args?.offset || args.offset === 0) {\n return incoming;\n }\n return {\n ...incoming,\n edges: [...(existing?.edges || []), ...incoming.edges],\n };\n },\n },\n developerOrgSales: {\n keyArgs: [\n \"organizationId\",\n \"applicationId\",\n \"status\",\n \"pricingModel\",\n \"isPaidOut\",\n ],\n merge(existing, incoming, { args }) {\n if (!args?.offset || args.offset === 0) {\n return incoming;\n }\n return {\n ...incoming,\n edges: [...(existing?.edges || []), ...incoming.edges],\n };\n },\n },\n developerOrgPayouts: {\n keyArgs: [\"organizationId\", \"status\"],\n merge(existing, incoming, { args }) {\n if (!args?.offset || args.offset === 0) {\n return incoming;\n }\n return {\n ...incoming,\n edges: [...(existing?.edges || []), ...incoming.edges],\n };\n },\n },\n developerOrgMembers: {\n keyArgs: [\"organizationId\", \"role\"],\n merge(existing, incoming, { args }) {\n if (!args?.offset || args.offset === 0) return incoming;\n return {\n ...incoming,\n edges: [...(existing?.edges || []), ...incoming.edges],\n };\n },\n },\n developerOrgInvitations: {\n keyArgs: [\"organizationId\", \"status\"],\n merge(existing, incoming, { args }) {\n if (!args?.offset || args.offset === 0) return incoming;\n return {\n ...incoming,\n edges: [...(existing?.edges || []), ...incoming.edges],\n };\n },\n },\n webhooks: {\n keyArgs: [\"applicationId\", \"status\"],\n merge(existing, incoming, { args }) {\n if (!args?.offset || args.offset === 0) return incoming;\n return {\n ...incoming,\n edges: [...(existing?.edges || []), ...incoming.edges],\n };\n },\n },\n webhookDeliveries: {\n keyArgs: [\"webhookId\", \"status\"],\n merge(existing, incoming, { args }) {\n if (!args?.offset || args.offset === 0) return incoming;\n return {\n ...incoming,\n edges: [...(existing?.edges || []), ...incoming.edges],\n };\n },\n },\n auditLogs: {\n keyArgs: [\"resource\", \"resourceId\", \"actorId\"],\n merge(existing, incoming, { args }) {\n if (!args?.offset || args.offset === 0) return incoming;\n return {\n ...incoming,\n edges: [...(existing?.edges || []), ...incoming.edges],\n };\n },\n },\n applicationVersions: {\n // TODO(phase1-backend): Switch to ApplicationVersionConnection cursor pagination\n // once backend ships connection type. See stability tracker.\n keyArgs: [\"applicationId\"],\n merge(existing, incoming, { args }) {\n if (!args?.offset || args.offset === 0) return incoming;\n if (Array.isArray(incoming) && Array.isArray(existing)) {\n return [...existing, ...incoming];\n }\n return incoming;\n },\n },\n },\n },\n WebhookDelivery: {\n keyFields: [\"id\"],\n },\n ApplicationVersion: {\n keyFields: [\"id\"],\n },\n DeveloperOrganization: {\n keyFields: [\"id\"],\n },\n Application: {\n keyFields: [\"id\"],\n },\n DeveloperOrgMember: {\n keyFields: [\"id\"],\n },\n DeveloperOrgInvitation: {\n keyFields: [\"id\"],\n },\n Webhook: {\n keyFields: [\"id\"],\n },\n ApiKey: {\n keyFields: [\"id\"],\n },\n OAuthClient: {\n keyFields: [\"id\"],\n },\n },\n }),\n defaultOptions: {\n watchQuery: {\n fetchPolicy: \"cache-and-network\",\n },\n query: {\n fetchPolicy: \"cache-first\",\n },\n },\n});\n\n/**\n * Error handling utilities\n */\nexport interface GraphQLErrorInfo {\n message: string;\n code?: string;\n path?: readonly (string | number)[];\n}\n\n// Type for Apollo errors (can be CombinedGraphQLErrors, ServerError, or Error)\nexport type ApolloErrorLike = Error & {\n graphQLErrors?: readonly {\n message: string;\n extensions?: { code?: string };\n path?: readonly (string | number)[];\n }[];\n networkError?: Error | null;\n};\n\nexport function parseGraphQLError(error: ApolloErrorLike): GraphQLErrorInfo[] {\n if (error instanceof CombinedGraphQLErrors) {\n return error.errors.map((err) => ({\n message: err.message,\n code: err.extensions?.code as string | undefined,\n path: err.path,\n }));\n }\n\n if (error.graphQLErrors && error.graphQLErrors.length > 0) {\n return error.graphQLErrors.map((err) => ({\n message: err.message,\n code: err.extensions?.code as string | undefined,\n path: err.path,\n }));\n }\n\n if (error instanceof ServerError || error.networkError) {\n return [\n {\n message: \"Network error. Please check your connection and try again.\",\n code: \"NETWORK_ERROR\",\n },\n ];\n }\n\n return [\n {\n message: error.message || \"An unexpected error occurred\",\n code: \"UNKNOWN_ERROR\",\n },\n ];\n}\n\n/**\n * Get a user-friendly error message from an Apollo error\n */\nexport function getErrorMessage(error: ApolloErrorLike): string {\n const errors = parseGraphQLError(error);\n return errors.map((e) => e.message).join(\", \");\n}\n\n/**\n * Check if an error is an authentication error\n */\nexport function isAuthError(error: ApolloErrorLike): boolean {\n const errors = parseGraphQLError(error);\n return errors.some(\n (e) => e.code === \"UNAUTHENTICATED\" || e.code === \"FORBIDDEN\",\n );\n}\n\n/**\n * Check if an error is a validation error\n */\nexport function isValidationError(error: ApolloErrorLike): boolean {\n const errors = parseGraphQLError(error);\n return errors.some(\n (e) => e.code === \"BAD_USER_INPUT\" || e.code === \"VALIDATION_ERROR\",\n );\n}\n\n/**\n * Clear Apollo cache (useful for logout)\n */\nexport async function clearApolloCache(): Promise<void> {\n await apolloClient.clearStore();\n}\n\n/**\n * Reset Apollo store and refetch queries\n */\nexport async function resetApolloStore(): Promise<void> {\n await apolloClient.resetStore();\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"DashboardPage.d.ts","sourceRoot":"","sources":["../../../src/devportal/pages/DashboardPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAa,MAAM,OAAO,CAAC;AAyB3C,eAAO,MAAM,aAAa,EAAE,EA6O3B,CAAC"}
1
+ {"version":3,"file":"DashboardPage.d.ts","sourceRoot":"","sources":["../../../src/devportal/pages/DashboardPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAa,MAAM,OAAO,CAAC;AA0B3C,eAAO,MAAM,aAAa,EAAE,EAgP3B,CAAC"}
@@ -6,135 +6,137 @@ import { PageLayout as i } from "../components/PageLayout.js";
6
6
  import { AppCard as a } from "../components/AppCard.js";
7
7
  import { PermissionGuard as o } from "../components/PermissionGuard.js";
8
8
  import "../components/index.js";
9
+ import { OnboardingChecklist as s } from "../components/OnboardingChecklist.js";
9
10
  import "../hooks/index.js";
10
- import { useEffect as s } from "react";
11
- import { AppWindow as c, ArrowRight as l, Building2 as u, DollarSign as d, Download as f, Loader2 as p, Plus as m } from "lucide-react";
12
- import { Fragment as h, jsx as g, jsxs as _ } from "react/jsx-runtime";
13
- import { GlassCard as v, GradientText as y } from "@burdenoff/fe-libs/ui";
11
+ import { useEffect as c } from "react";
12
+ import { AppWindow as l, ArrowRight as u, Building2 as d, DollarSign as f, Download as p, Loader2 as m, Plus as h } from "lucide-react";
13
+ import { Fragment as g, jsx as _, jsxs as v } from "react/jsx-runtime";
14
+ import { GlassCard as y, GradientText as b } from "@burdenoff/fe-libs/ui";
14
15
  //#region src/devportal/pages/DashboardPage.tsx
15
- var b = () => {
16
- let { navigate: d, basePath: f, activeOrgId: v, setActiveOrgId: b } = e(), w = t(), { data: T, loading: E } = r({
16
+ var x = () => {
17
+ let { navigate: f, basePath: p, activeOrgId: y, setActiveOrgId: x } = e(), T = t(), { data: E, loading: D } = r({
17
18
  variables: {},
18
19
  fetchPolicy: "network-only"
19
- }), D = (T?.myDeveloperOrganizations ?? []).filter((e) => e.kind !== "PHYSICAL"), O = (() => {
20
- if (D.length) return v && D.some((e) => e.id === v) ? v : D[0]?.id;
20
+ }), O = (E?.myDeveloperOrganizations ?? []).filter((e) => e.kind !== "PHYSICAL"), k = (() => {
21
+ if (O.length) return y && O.some((e) => e.id === y) ? y : O[0]?.id;
21
22
  })();
22
- s(() => {
23
- O && O !== v && b(O);
23
+ c(() => {
24
+ k && k !== y && x(k);
24
25
  }, [
25
- O,
26
- v,
27
- b
26
+ k,
27
+ y,
28
+ x
28
29
  ]);
29
- let { data: k, loading: A } = r({
30
- variables: { organizationId: O },
31
- skip: !O,
30
+ let { data: A, loading: j } = r({
31
+ variables: { organizationId: k },
32
+ skip: !k,
32
33
  fetchPolicy: "cache-and-network"
33
- }), j = E || A, M = k ?? T;
34
- s(() => {
35
- if (!(E || !T) && !(D.length > 0)) {
34
+ }), M = D || j, N = A ?? E;
35
+ c(() => {
36
+ if (!(D || !E) && !(O.length > 0)) {
36
37
  try {
37
38
  if (localStorage.getItem("devportal_setup_dismissed")) return;
38
39
  } catch {
39
40
  return;
40
41
  }
41
- d?.(`${f}/setup`);
42
+ f?.(`${p}/setup`);
42
43
  }
43
44
  }, [
45
+ D,
44
46
  E,
45
- T,
46
- D.length,
47
- d,
48
- f
47
+ O.length,
48
+ f,
49
+ p
49
50
  ]);
50
- let N = k?.applications?.edges?.map((e) => e.node) ?? [], P = M?.applications?.totalCount ?? 0, F = D.length, I = (e) => {
51
- d?.(`${f}${e}`);
52
- }, L = j, R = !E && D.length > 0;
53
- return /* @__PURE__ */ g(o, {
51
+ let P = A?.applications?.edges?.map((e) => e.node) ?? [], F = N?.applications?.totalCount ?? 0, I = O.length, L = (e) => {
52
+ f?.(`${p}${e}`);
53
+ }, R = M, z = !D && O.length > 0;
54
+ return /* @__PURE__ */ _(o, {
54
55
  authOnly: !0,
55
- children: /* @__PURE__ */ g(i, {
56
- title: /* @__PURE__ */ _(h, { children: [
57
- w("devportal.developer", "Developer"),
56
+ children: /* @__PURE__ */ _(i, {
57
+ title: /* @__PURE__ */ v(g, { children: [
58
+ T("devportal.developer", "Developer"),
58
59
  " ",
59
- /* @__PURE__ */ g(y, { children: w("devportal.portal", "Portal") })
60
+ /* @__PURE__ */ _(b, { children: T("devportal.portal", "Portal") })
60
61
  ] }),
61
- actions: R ? /* @__PURE__ */ _("button", {
62
+ actions: z ? /* @__PURE__ */ v("button", {
62
63
  "data-tour": "devportal-dashboard-create-app-button",
63
- onClick: () => I("/apps/new"),
64
+ onClick: () => L("/apps/new"),
64
65
  className: "inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors",
65
- children: [/* @__PURE__ */ g(m, { size: 16 }), w("devportal.createApp", "Create App")]
66
- }) : L ? null : /* @__PURE__ */ _("button", {
66
+ children: [/* @__PURE__ */ _(h, { size: 16 }), T("devportal.createApp", "Create App")]
67
+ }) : R ? null : /* @__PURE__ */ v("button", {
67
68
  "data-tour": "devportal-dashboard-create-org-button",
68
- onClick: () => I("/organizations/new"),
69
+ onClick: () => L("/organizations/new"),
69
70
  className: "inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors",
70
- children: [/* @__PURE__ */ g(m, { size: 16 }), w("devportal.createOrganization", "Create Organization")]
71
+ children: [/* @__PURE__ */ _(h, { size: 16 }), T("devportal.createOrganization", "Create Organization")]
71
72
  }),
72
- children: /* @__PURE__ */ _("div", {
73
+ children: /* @__PURE__ */ v("div", {
73
74
  className: "flex flex-col gap-8",
74
75
  children: [
75
- /* @__PURE__ */ g("div", {
76
+ /* @__PURE__ */ _(s, { onNavigate: L }),
77
+ /* @__PURE__ */ _("div", {
76
78
  className: "flex items-center gap-6 border-b border-border-seam pb-6",
77
- children: L ? /* @__PURE__ */ g(p, { className: "h-4 w-4 animate-spin text-text-secondary" }) : /* @__PURE__ */ _(h, { children: [
78
- /* @__PURE__ */ g(x, {
79
- icon: /* @__PURE__ */ g(c, { size: 14 }),
80
- value: P,
81
- label: w("devportal.apps", "Apps")
82
- }),
83
- /* @__PURE__ */ g("div", { className: "h-4 w-px bg-border-seam" }),
84
- /* @__PURE__ */ g(x, {
85
- icon: /* @__PURE__ */ g(u, { size: 14 }),
79
+ children: R ? /* @__PURE__ */ _(m, { className: "h-4 w-4 animate-spin text-text-secondary" }) : /* @__PURE__ */ v(g, { children: [
80
+ /* @__PURE__ */ _(S, {
81
+ icon: /* @__PURE__ */ _(l, { size: 14 }),
86
82
  value: F,
87
- label: w("devportal.organizations", "Organizations")
83
+ label: T("devportal.apps", "Apps")
84
+ }),
85
+ /* @__PURE__ */ _("div", { className: "h-4 w-px bg-border-seam" }),
86
+ /* @__PURE__ */ _(S, {
87
+ icon: /* @__PURE__ */ _(d, { size: 14 }),
88
+ value: I,
89
+ label: T("devportal.organizations", "Organizations")
88
90
  })
89
91
  ] })
90
92
  }),
91
- /* @__PURE__ */ g(S, {
92
- title: w("devportal.recentApplications", "Recent Applications"),
93
- action: N.length > 0 ? /* @__PURE__ */ _("button", {
94
- onClick: () => I("/apps"),
93
+ /* @__PURE__ */ _(C, {
94
+ title: T("devportal.recentApplications", "Recent Applications"),
95
+ action: P.length > 0 ? /* @__PURE__ */ v("button", {
96
+ onClick: () => L("/apps"),
95
97
  className: "inline-flex items-center gap-1 text-sm font-medium text-primary hover:underline",
96
- children: [w("devportal.viewAll", "View all"), /* @__PURE__ */ g(l, { size: 14 })]
98
+ children: [T("devportal.viewAll", "View all"), /* @__PURE__ */ _(u, { size: 14 })]
97
99
  }) : void 0,
98
- children: L ? /* @__PURE__ */ g("div", {
100
+ children: R ? /* @__PURE__ */ _("div", {
99
101
  className: "flex items-center justify-center py-12",
100
- children: /* @__PURE__ */ g(p, { className: "h-6 w-6 animate-spin text-text-secondary" })
101
- }) : N.length > 0 ? /* @__PURE__ */ g("div", {
102
+ children: /* @__PURE__ */ _(m, { className: "h-6 w-6 animate-spin text-text-secondary" })
103
+ }) : P.length > 0 ? /* @__PURE__ */ _("div", {
102
104
  className: "grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3",
103
- children: N.slice(0, 3).map((e) => /* @__PURE__ */ g(a, {
105
+ children: P.slice(0, 3).map((e) => /* @__PURE__ */ _(a, {
104
106
  app: e,
105
- onClick: () => I(`/apps/${e.id}`),
107
+ onClick: () => L(`/apps/${e.id}`),
106
108
  showActions: !1
107
109
  }, e.id))
108
- }) : /* @__PURE__ */ g(n, {
110
+ }) : /* @__PURE__ */ _(n, {
109
111
  illustration: "empty-generic",
110
- title: w("devportal.noApplicationsYet", "No applications yet"),
111
- description: R ? w("devportal.createFirstApplicationToGetStarted", "Create your first application to get started") : w("devportal.createOrgFirstToPublishApps", "Create an organization first to start publishing apps"),
112
- action: R ? /* @__PURE__ */ _("button", {
113
- onClick: () => I("/apps/new"),
112
+ title: T("devportal.noApplicationsYet", "No applications yet"),
113
+ description: z ? T("devportal.createFirstApplicationToGetStarted", "Create your first application to get started") : T("devportal.createOrgFirstToPublishApps", "Create an organization first to start publishing apps"),
114
+ action: z ? /* @__PURE__ */ v("button", {
115
+ onClick: () => L("/apps/new"),
114
116
  className: "inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90",
115
- children: [/* @__PURE__ */ g(m, { size: 16 }), w("devportal.createApp", "Create App")]
116
- }) : /* @__PURE__ */ _("button", {
117
- onClick: () => I("/organizations/new"),
117
+ children: [/* @__PURE__ */ _(h, { size: 16 }), T("devportal.createApp", "Create App")]
118
+ }) : /* @__PURE__ */ v("button", {
119
+ onClick: () => L("/organizations/new"),
118
120
  className: "inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90",
119
- children: [/* @__PURE__ */ g(m, { size: 16 }), w("devportal.createOrganization", "Create Organization")]
121
+ children: [/* @__PURE__ */ _(h, { size: 16 }), T("devportal.createOrganization", "Create Organization")]
120
122
  })
121
123
  })
122
124
  }),
123
- (L || D.length > 0) && /* @__PURE__ */ g(S, {
124
- title: w("devportal.yourOrganizations", "Your Organizations"),
125
- action: D.length > 0 ? /* @__PURE__ */ _("button", {
126
- onClick: () => I("/organizations"),
125
+ (R || O.length > 0) && /* @__PURE__ */ _(C, {
126
+ title: T("devportal.yourOrganizations", "Your Organizations"),
127
+ action: O.length > 0 ? /* @__PURE__ */ v("button", {
128
+ onClick: () => L("/organizations"),
127
129
  className: "inline-flex items-center gap-1 text-sm font-medium text-primary hover:underline",
128
- children: [w("devportal.viewAll", "View all"), /* @__PURE__ */ g(l, { size: 14 })]
130
+ children: [T("devportal.viewAll", "View all"), /* @__PURE__ */ _(u, { size: 14 })]
129
131
  }) : void 0,
130
- children: L ? /* @__PURE__ */ g("div", {
132
+ children: R ? /* @__PURE__ */ _("div", {
131
133
  className: "flex items-center justify-center py-12",
132
- children: /* @__PURE__ */ g(p, { className: "h-6 w-6 animate-spin text-text-secondary" })
133
- }) : /* @__PURE__ */ g("div", {
134
+ children: /* @__PURE__ */ _(m, { className: "h-6 w-6 animate-spin text-text-secondary" })
135
+ }) : /* @__PURE__ */ _("div", {
134
136
  className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3",
135
- children: D.slice(0, 3).map((e) => /* @__PURE__ */ g(C, {
137
+ children: O.slice(0, 3).map((e) => /* @__PURE__ */ _(w, {
136
138
  org: e,
137
- onClick: () => I(`/organizations/${e.id}`)
139
+ onClick: () => L(`/organizations/${e.id}`)
138
140
  }, e.id))
139
141
  })
140
142
  })
@@ -142,34 +144,34 @@ var b = () => {
142
144
  })
143
145
  })
144
146
  });
145
- }, x = ({ icon: e, value: t, label: n }) => /* @__PURE__ */ _("div", {
147
+ }, S = ({ icon: e, value: t, label: n }) => /* @__PURE__ */ v("div", {
146
148
  className: "flex items-center gap-1.5",
147
149
  children: [
148
- /* @__PURE__ */ g("span", {
150
+ /* @__PURE__ */ _("span", {
149
151
  className: "text-text-muted",
150
152
  children: e
151
153
  }),
152
- /* @__PURE__ */ g("span", {
154
+ /* @__PURE__ */ _("span", {
153
155
  className: "text-sm font-semibold tabular-nums",
154
156
  children: t
155
157
  }),
156
- /* @__PURE__ */ g("span", {
158
+ /* @__PURE__ */ _("span", {
157
159
  className: "text-sm text-text-secondary",
158
160
  children: n
159
161
  })
160
162
  ]
161
- }), S = ({ title: e, action: t, children: n }) => /* @__PURE__ */ _("section", {
163
+ }), C = ({ title: e, action: t, children: n }) => /* @__PURE__ */ v("section", {
162
164
  className: "flex flex-col gap-4",
163
- children: [/* @__PURE__ */ _("div", {
165
+ children: [/* @__PURE__ */ v("div", {
164
166
  className: "flex items-center justify-between",
165
- children: [/* @__PURE__ */ g("h2", {
167
+ children: [/* @__PURE__ */ _("h2", {
166
168
  className: "text-sm font-semibold uppercase tracking-wider text-text-secondary",
167
169
  children: e
168
170
  }), t]
169
171
  }), n]
170
- }), C = ({ org: e, onClick: n }) => {
172
+ }), w = ({ org: e, onClick: n }) => {
171
173
  let r = t(), i = e.name.split(" ").map((e) => e[0]).join("").slice(0, 2).toUpperCase();
172
- return /* @__PURE__ */ _(v, {
174
+ return /* @__PURE__ */ v(y, {
173
175
  treatment: "insight",
174
176
  role: "button",
175
177
  tabIndex: 0,
@@ -178,23 +180,23 @@ var b = () => {
178
180
  onKeyDown: (e) => {
179
181
  (e.key === "Enter" || e.key === " ") && (e.preventDefault(), n());
180
182
  },
181
- children: [/* @__PURE__ */ _("div", {
183
+ children: [/* @__PURE__ */ v("div", {
182
184
  className: "flex items-center gap-3 mb-4",
183
185
  children: [
184
- e.logoUrl ? /* @__PURE__ */ g("img", {
186
+ e.logoUrl ? /* @__PURE__ */ _("img", {
185
187
  src: e.logoUrl,
186
188
  alt: e.name,
187
189
  className: "h-10 w-10 rounded-xl object-cover"
188
- }) : /* @__PURE__ */ g("div", {
190
+ }) : /* @__PURE__ */ _("div", {
189
191
  className: "flex h-10 w-10 items-center justify-center rounded-xl bg-action-primary-bg/15 text-primary text-sm font-bold",
190
192
  children: i
191
193
  }),
192
- /* @__PURE__ */ _("div", {
194
+ /* @__PURE__ */ v("div", {
193
195
  className: "min-w-0 flex-1",
194
- children: [/* @__PURE__ */ g("p", {
196
+ children: [/* @__PURE__ */ _("p", {
195
197
  className: "font-semibold truncate text-text-primary",
196
198
  children: e.name
197
- }), /* @__PURE__ */ _("p", {
199
+ }), /* @__PURE__ */ v("p", {
198
200
  className: "text-xs text-text-secondary mt-0.5",
199
201
  children: [
200
202
  e.publishedApps ?? 0,
@@ -203,34 +205,34 @@ var b = () => {
203
205
  ]
204
206
  })]
205
207
  }),
206
- /* @__PURE__ */ g(l, {
208
+ /* @__PURE__ */ _(u, {
207
209
  size: 15,
208
210
  className: "text-text-muted shrink-0"
209
211
  })
210
212
  ]
211
- }), /* @__PURE__ */ _("div", {
213
+ }), /* @__PURE__ */ v("div", {
212
214
  className: "grid grid-cols-2 gap-3",
213
- children: [/* @__PURE__ */ _("div", {
215
+ children: [/* @__PURE__ */ v("div", {
214
216
  className: "flex items-center gap-2 rounded-lg bg-bg-muted px-3 py-2",
215
- children: [/* @__PURE__ */ g(f, {
217
+ children: [/* @__PURE__ */ _(p, {
216
218
  size: 12,
217
219
  className: "text-text-muted shrink-0"
218
- }), /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("p", {
220
+ }), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("p", {
219
221
  className: "text-[11px] text-text-secondary leading-none mb-0.5",
220
222
  children: r("devportal.installs", "Installs")
221
- }), /* @__PURE__ */ g("p", {
223
+ }), /* @__PURE__ */ _("p", {
222
224
  className: "text-sm font-semibold tabular-nums",
223
225
  children: (e.totalInstalls || 0).toLocaleString()
224
226
  })] })]
225
- }), /* @__PURE__ */ _("div", {
227
+ }), /* @__PURE__ */ v("div", {
226
228
  className: "flex items-center gap-2 rounded-lg bg-bg-muted px-3 py-2",
227
- children: [/* @__PURE__ */ g(d, {
229
+ children: [/* @__PURE__ */ _(f, {
228
230
  size: 12,
229
231
  className: "text-text-muted shrink-0"
230
- }), /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("p", {
232
+ }), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("p", {
231
233
  className: "text-[11px] text-text-secondary leading-none mb-0.5",
232
234
  children: r("devportal.revenue", "Revenue")
233
- }), /* @__PURE__ */ _("p", {
235
+ }), /* @__PURE__ */ v("p", {
234
236
  className: "text-sm font-semibold tabular-nums",
235
237
  children: ["$", (e.totalRevenue || 0).toLocaleString()]
236
238
  })] })]
@@ -239,6 +241,6 @@ var b = () => {
239
241
  });
240
242
  };
241
243
  //#endregion
242
- export { b as DashboardPage };
244
+ export { x as DashboardPage };
243
245
 
244
246
  //# sourceMappingURL=DashboardPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DashboardPage.js","names":[],"sources":["../../../src/devportal/pages/DashboardPage.tsx"],"sourcesContent":["import type { FC, ReactNode } from \"react\";\nimport { useEffect } from \"react\";\nimport {\n AppWindow,\n Building2,\n Plus,\n ArrowRight,\n Loader2,\n Download,\n DollarSign,\n} from \"lucide-react\";\nimport { GlassCard, GradientText } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../components/PageLayout\";\nimport { AppCard } from \"../components/AppCard\";\nimport { EmptyState } from \"../components/EmptyState\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { PermissionGuard } from \"../components\";\nimport { useTr } from \"../utils/i18n\";\nimport type { Application, DeveloperOrganization } from \"../types\";\nimport {\n useGetDevportalDashboardBootstrapQuery,\n type OrganizationFieldsFragment,\n type ApplicationFieldsFragment,\n} from \"../hooks\";\n\nexport const DashboardPage: FC = () => {\n const { navigate, basePath, activeOrgId, setActiveOrgId } = useDevportal();\n const tr = useTr();\n\n // network-only prevents a stale empty-orgs cache entry from\n // firing the /setup redirect before the real response arrives.\n const { data: orgsData, loading: orgsLoading } =\n useGetDevportalDashboardBootstrapQuery({\n variables: {},\n fetchPolicy: \"network-only\",\n });\n\n // This dashboard is only ever mounted at the plain (non-seller) devportal\n // route — exclude PHYSICAL orgs so a seller-portal org never shows up\n // here. See DeveloperOrgKind (BOFF-7285).\n const organizations: OrganizationFieldsFragment[] = (\n orgsData?.myDeveloperOrganizations ?? []\n ).filter((org: OrganizationFieldsFragment) => org.kind !== \"PHYSICAL\");\n\n const resolvedOrgId: string | undefined = (() => {\n if (!organizations.length) return undefined;\n if (activeOrgId && organizations.some((o) => o.id === activeOrgId))\n return activeOrgId;\n return organizations[0]?.id;\n })();\n\n useEffect(() => {\n if (resolvedOrgId && resolvedOrgId !== activeOrgId) {\n setActiveOrgId(resolvedOrgId);\n }\n }, [resolvedOrgId, activeOrgId, setActiveOrgId]);\n\n const { data: appsData, loading: appsLoading } =\n useGetDevportalDashboardBootstrapQuery({\n variables: { organizationId: resolvedOrgId },\n skip: !resolvedOrgId,\n fetchPolicy: \"cache-and-network\",\n });\n\n const loading = orgsLoading || appsLoading;\n const data = appsData ?? orgsData;\n\n useEffect(() => {\n if (orgsLoading || !orgsData) return;\n if (organizations.length > 0) return;\n try {\n if (localStorage.getItem(\"devportal_setup_dismissed\")) return;\n } catch {\n return;\n }\n navigate?.(`${basePath}/setup`);\n }, [orgsLoading, orgsData, organizations.length, navigate, basePath]);\n\n const applications: ApplicationFieldsFragment[] =\n appsData?.applications?.edges?.map(\n (e: { node: ApplicationFieldsFragment }) => e.node,\n ) ?? [];\n\n const totalApps = data?.applications?.totalCount ?? 0;\n const totalOrgs = organizations.length;\n\n const handleNavigate = (path: string) => {\n navigate?.(`${basePath}${path}`);\n };\n\n const isLoading = loading;\n // hasOrgs depends only on the orgs query — not on appsLoading — so the\n // header action doesn't flash \"Create Organization\" while apps are loading.\n const hasOrgs = !orgsLoading && organizations.length > 0;\n\n return (\n <PermissionGuard authOnly>\n <PageLayout\n title={\n <>\n {tr(\"devportal.developer\", \"Developer\")}{\" \"}\n <GradientText>{tr(\"devportal.portal\", \"Portal\")}</GradientText>\n </>\n }\n actions={\n hasOrgs ? (\n <button\n data-tour=\"devportal-dashboard-create-app-button\"\n onClick={() => handleNavigate(\"/apps/new\")}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors\"\n >\n <Plus size={16} />\n {tr(\"devportal.createApp\", \"Create App\")}\n </button>\n ) : !isLoading ? (\n <button\n data-tour=\"devportal-dashboard-create-org-button\"\n onClick={() => handleNavigate(\"/organizations/new\")}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors\"\n >\n <Plus size={16} />\n {tr(\"devportal.createOrganization\", \"Create Organization\")}\n </button>\n ) : null\n }\n >\n <div className=\"flex flex-col gap-8\">\n {/* Compact inline stats */}\n <div className=\"flex items-center gap-6 border-b border-border-seam pb-6\">\n {isLoading ? (\n <Loader2 className=\"h-4 w-4 animate-spin text-text-secondary\" />\n ) : (\n <>\n <StatPill\n icon={<AppWindow size={14} />}\n value={totalApps}\n label={tr(\"devportal.apps\", \"Apps\")}\n />\n <div className=\"h-4 w-px bg-border-seam\" />\n <StatPill\n icon={<Building2 size={14} />}\n value={totalOrgs}\n label={tr(\"devportal.organizations\", \"Organizations\")}\n />\n </>\n )}\n </div>\n\n {/* Recent Apps */}\n <DashSection\n title={tr(\"devportal.recentApplications\", \"Recent Applications\")}\n action={\n applications.length > 0 ? (\n <button\n onClick={() => handleNavigate(\"/apps\")}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n {tr(\"devportal.viewAll\", \"View all\")}\n <ArrowRight size={14} />\n </button>\n ) : undefined\n }\n >\n {isLoading ? (\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"h-6 w-6 animate-spin text-text-secondary\" />\n </div>\n ) : applications.length > 0 ? (\n <div className=\"grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3\">\n {applications\n .slice(0, 3)\n .map((app: ApplicationFieldsFragment) => (\n <AppCard\n key={app.id}\n app={app as Application}\n onClick={() => handleNavigate(`/apps/${app.id}`)}\n showActions={false}\n />\n ))}\n </div>\n ) : (\n <EmptyState\n illustration=\"empty-generic\"\n title={tr(\"devportal.noApplicationsYet\", \"No applications yet\")}\n description={\n hasOrgs\n ? tr(\n \"devportal.createFirstApplicationToGetStarted\",\n \"Create your first application to get started\",\n )\n : tr(\n \"devportal.createOrgFirstToPublishApps\",\n \"Create an organization first to start publishing apps\",\n )\n }\n action={\n hasOrgs ? (\n <button\n onClick={() => handleNavigate(\"/apps/new\")}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <Plus size={16} />\n {tr(\"devportal.createApp\", \"Create App\")}\n </button>\n ) : (\n <button\n onClick={() => handleNavigate(\"/organizations/new\")}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <Plus size={16} />\n {tr(\n \"devportal.createOrganization\",\n \"Create Organization\",\n )}\n </button>\n )\n }\n />\n )}\n </DashSection>\n\n {/* Organizations */}\n {(isLoading || organizations.length > 0) && (\n <DashSection\n title={tr(\"devportal.yourOrganizations\", \"Your Organizations\")}\n action={\n organizations.length > 0 ? (\n <button\n onClick={() => handleNavigate(\"/organizations\")}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n {tr(\"devportal.viewAll\", \"View all\")}\n <ArrowRight size={14} />\n </button>\n ) : undefined\n }\n >\n {isLoading ? (\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"h-6 w-6 animate-spin text-text-secondary\" />\n </div>\n ) : (\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {organizations\n .slice(0, 3)\n .map((org: OrganizationFieldsFragment) => (\n <OrgCard\n key={org.id}\n org={org as DeveloperOrganization}\n onClick={() =>\n handleNavigate(`/organizations/${org.id}`)\n }\n />\n ))}\n </div>\n )}\n </DashSection>\n )}\n </div>\n </PageLayout>\n </PermissionGuard>\n );\n};\n\nconst StatPill: FC<{ icon: ReactNode; value: number; label: string }> = ({\n icon,\n value,\n label,\n}) => (\n <div className=\"flex items-center gap-1.5\">\n <span className=\"text-text-muted\">{icon}</span>\n <span className=\"text-sm font-semibold tabular-nums\">{value}</span>\n <span className=\"text-sm text-text-secondary\">{label}</span>\n </div>\n);\n\nconst DashSection: FC<{\n title: string;\n action?: ReactNode;\n children: ReactNode;\n}> = ({ title, action, children }) => (\n <section className=\"flex flex-col gap-4\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-sm font-semibold uppercase tracking-wider text-text-secondary\">\n {title}\n </h2>\n {action}\n </div>\n {children}\n </section>\n);\n\nconst OrgCard: FC<{ org: DeveloperOrganization; onClick: () => void }> = ({\n org,\n onClick,\n}) => {\n const tr = useTr();\n\n const initials = org.name\n .split(\" \")\n .map((w: string) => w[0])\n .join(\"\")\n .slice(0, 2)\n .toUpperCase();\n\n return (\n <GlassCard\n treatment=\"insight\"\n role=\"button\"\n tabIndex={0}\n className=\"cursor-pointer p-5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n onClick={onClick}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onClick();\n }\n }}\n >\n <div className=\"flex items-center gap-3 mb-4\">\n {org.logoUrl ? (\n <img\n src={org.logoUrl}\n alt={org.name}\n className=\"h-10 w-10 rounded-xl object-cover\"\n />\n ) : (\n <div className=\"flex h-10 w-10 items-center justify-center rounded-xl bg-action-primary-bg/15 text-primary text-sm font-bold\">\n {initials}\n </div>\n )}\n <div className=\"min-w-0 flex-1\">\n <p className=\"font-semibold truncate text-text-primary\">{org.name}</p>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n {org.publishedApps ?? 0}{\" \"}\n {tr(\"devportal.appsPublished\", \"apps published\")}\n </p>\n </div>\n <ArrowRight size={15} className=\"text-text-muted shrink-0\" />\n </div>\n\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"flex items-center gap-2 rounded-lg bg-bg-muted px-3 py-2\">\n <Download size={12} className=\"text-text-muted shrink-0\" />\n <div>\n <p className=\"text-[11px] text-text-secondary leading-none mb-0.5\">\n {tr(\"devportal.installs\", \"Installs\")}\n </p>\n <p className=\"text-sm font-semibold tabular-nums\">\n {(org.totalInstalls || 0).toLocaleString()}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2 rounded-lg bg-bg-muted px-3 py-2\">\n <DollarSign size={12} className=\"text-text-muted shrink-0\" />\n <div>\n <p className=\"text-[11px] text-text-secondary leading-none mb-0.5\">\n {tr(\"devportal.revenue\", \"Revenue\")}\n </p>\n <p className=\"text-sm font-semibold tabular-nums\">\n ${(org.totalRevenue || 0).toLocaleString()}\n </p>\n </div>\n </div>\n </div>\n </GlassCard>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"DashboardPage.js","names":[],"sources":["../../../src/devportal/pages/DashboardPage.tsx"],"sourcesContent":["import type { FC, ReactNode } from \"react\";\nimport { useEffect } from \"react\";\nimport {\n AppWindow,\n Building2,\n Plus,\n ArrowRight,\n Loader2,\n Download,\n DollarSign,\n} from \"lucide-react\";\nimport { GlassCard, GradientText } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../components/PageLayout\";\nimport { AppCard } from \"../components/AppCard\";\nimport { EmptyState } from \"../components/EmptyState\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { PermissionGuard } from \"../components\";\nimport { OnboardingChecklist } from \"../components/OnboardingChecklist\";\nimport { useTr } from \"../utils/i18n\";\nimport type { Application, DeveloperOrganization } from \"../types\";\nimport {\n useGetDevportalDashboardBootstrapQuery,\n type OrganizationFieldsFragment,\n type ApplicationFieldsFragment,\n} from \"../hooks\";\n\nexport const DashboardPage: FC = () => {\n const { navigate, basePath, activeOrgId, setActiveOrgId } = useDevportal();\n const tr = useTr();\n\n // network-only prevents a stale empty-orgs cache entry from\n // firing the /setup redirect before the real response arrives.\n const { data: orgsData, loading: orgsLoading } =\n useGetDevportalDashboardBootstrapQuery({\n variables: {},\n fetchPolicy: \"network-only\",\n });\n\n // This dashboard is only ever mounted at the plain (non-seller) devportal\n // route — exclude PHYSICAL orgs so a seller-portal org never shows up\n // here. See DeveloperOrgKind (BOFF-7285).\n const organizations: OrganizationFieldsFragment[] = (\n orgsData?.myDeveloperOrganizations ?? []\n ).filter((org: OrganizationFieldsFragment) => org.kind !== \"PHYSICAL\");\n\n const resolvedOrgId: string | undefined = (() => {\n if (!organizations.length) return undefined;\n if (activeOrgId && organizations.some((o) => o.id === activeOrgId))\n return activeOrgId;\n return organizations[0]?.id;\n })();\n\n useEffect(() => {\n if (resolvedOrgId && resolvedOrgId !== activeOrgId) {\n setActiveOrgId(resolvedOrgId);\n }\n }, [resolvedOrgId, activeOrgId, setActiveOrgId]);\n\n const { data: appsData, loading: appsLoading } =\n useGetDevportalDashboardBootstrapQuery({\n variables: { organizationId: resolvedOrgId },\n skip: !resolvedOrgId,\n fetchPolicy: \"cache-and-network\",\n });\n\n const loading = orgsLoading || appsLoading;\n const data = appsData ?? orgsData;\n\n useEffect(() => {\n if (orgsLoading || !orgsData) return;\n if (organizations.length > 0) return;\n try {\n if (localStorage.getItem(\"devportal_setup_dismissed\")) return;\n } catch {\n return;\n }\n navigate?.(`${basePath}/setup`);\n }, [orgsLoading, orgsData, organizations.length, navigate, basePath]);\n\n const applications: ApplicationFieldsFragment[] =\n appsData?.applications?.edges?.map(\n (e: { node: ApplicationFieldsFragment }) => e.node,\n ) ?? [];\n\n const totalApps = data?.applications?.totalCount ?? 0;\n const totalOrgs = organizations.length;\n\n const handleNavigate = (path: string) => {\n navigate?.(`${basePath}${path}`);\n };\n\n const isLoading = loading;\n // hasOrgs depends only on the orgs query — not on appsLoading — so the\n // header action doesn't flash \"Create Organization\" while apps are loading.\n const hasOrgs = !orgsLoading && organizations.length > 0;\n\n return (\n <PermissionGuard authOnly>\n <PageLayout\n title={\n <>\n {tr(\"devportal.developer\", \"Developer\")}{\" \"}\n <GradientText>{tr(\"devportal.portal\", \"Portal\")}</GradientText>\n </>\n }\n actions={\n hasOrgs ? (\n <button\n data-tour=\"devportal-dashboard-create-app-button\"\n onClick={() => handleNavigate(\"/apps/new\")}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors\"\n >\n <Plus size={16} />\n {tr(\"devportal.createApp\", \"Create App\")}\n </button>\n ) : !isLoading ? (\n <button\n data-tour=\"devportal-dashboard-create-org-button\"\n onClick={() => handleNavigate(\"/organizations/new\")}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors\"\n >\n <Plus size={16} />\n {tr(\"devportal.createOrganization\", \"Create Organization\")}\n </button>\n ) : null\n }\n >\n <div className=\"flex flex-col gap-8\">\n {/* Developer onboarding checklist — hides itself once complete */}\n <OnboardingChecklist onNavigate={handleNavigate} />\n\n {/* Compact inline stats */}\n <div className=\"flex items-center gap-6 border-b border-border-seam pb-6\">\n {isLoading ? (\n <Loader2 className=\"h-4 w-4 animate-spin text-text-secondary\" />\n ) : (\n <>\n <StatPill\n icon={<AppWindow size={14} />}\n value={totalApps}\n label={tr(\"devportal.apps\", \"Apps\")}\n />\n <div className=\"h-4 w-px bg-border-seam\" />\n <StatPill\n icon={<Building2 size={14} />}\n value={totalOrgs}\n label={tr(\"devportal.organizations\", \"Organizations\")}\n />\n </>\n )}\n </div>\n\n {/* Recent Apps */}\n <DashSection\n title={tr(\"devportal.recentApplications\", \"Recent Applications\")}\n action={\n applications.length > 0 ? (\n <button\n onClick={() => handleNavigate(\"/apps\")}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n {tr(\"devportal.viewAll\", \"View all\")}\n <ArrowRight size={14} />\n </button>\n ) : undefined\n }\n >\n {isLoading ? (\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"h-6 w-6 animate-spin text-text-secondary\" />\n </div>\n ) : applications.length > 0 ? (\n <div className=\"grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3\">\n {applications\n .slice(0, 3)\n .map((app: ApplicationFieldsFragment) => (\n <AppCard\n key={app.id}\n app={app as Application}\n onClick={() => handleNavigate(`/apps/${app.id}`)}\n showActions={false}\n />\n ))}\n </div>\n ) : (\n <EmptyState\n illustration=\"empty-generic\"\n title={tr(\"devportal.noApplicationsYet\", \"No applications yet\")}\n description={\n hasOrgs\n ? tr(\n \"devportal.createFirstApplicationToGetStarted\",\n \"Create your first application to get started\",\n )\n : tr(\n \"devportal.createOrgFirstToPublishApps\",\n \"Create an organization first to start publishing apps\",\n )\n }\n action={\n hasOrgs ? (\n <button\n onClick={() => handleNavigate(\"/apps/new\")}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <Plus size={16} />\n {tr(\"devportal.createApp\", \"Create App\")}\n </button>\n ) : (\n <button\n onClick={() => handleNavigate(\"/organizations/new\")}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <Plus size={16} />\n {tr(\n \"devportal.createOrganization\",\n \"Create Organization\",\n )}\n </button>\n )\n }\n />\n )}\n </DashSection>\n\n {/* Organizations */}\n {(isLoading || organizations.length > 0) && (\n <DashSection\n title={tr(\"devportal.yourOrganizations\", \"Your Organizations\")}\n action={\n organizations.length > 0 ? (\n <button\n onClick={() => handleNavigate(\"/organizations\")}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n {tr(\"devportal.viewAll\", \"View all\")}\n <ArrowRight size={14} />\n </button>\n ) : undefined\n }\n >\n {isLoading ? (\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"h-6 w-6 animate-spin text-text-secondary\" />\n </div>\n ) : (\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {organizations\n .slice(0, 3)\n .map((org: OrganizationFieldsFragment) => (\n <OrgCard\n key={org.id}\n org={org as DeveloperOrganization}\n onClick={() =>\n handleNavigate(`/organizations/${org.id}`)\n }\n />\n ))}\n </div>\n )}\n </DashSection>\n )}\n </div>\n </PageLayout>\n </PermissionGuard>\n );\n};\n\nconst StatPill: FC<{ icon: ReactNode; value: number; label: string }> = ({\n icon,\n value,\n label,\n}) => (\n <div className=\"flex items-center gap-1.5\">\n <span className=\"text-text-muted\">{icon}</span>\n <span className=\"text-sm font-semibold tabular-nums\">{value}</span>\n <span className=\"text-sm text-text-secondary\">{label}</span>\n </div>\n);\n\nconst DashSection: FC<{\n title: string;\n action?: ReactNode;\n children: ReactNode;\n}> = ({ title, action, children }) => (\n <section className=\"flex flex-col gap-4\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-sm font-semibold uppercase tracking-wider text-text-secondary\">\n {title}\n </h2>\n {action}\n </div>\n {children}\n </section>\n);\n\nconst OrgCard: FC<{ org: DeveloperOrganization; onClick: () => void }> = ({\n org,\n onClick,\n}) => {\n const tr = useTr();\n\n const initials = org.name\n .split(\" \")\n .map((w: string) => w[0])\n .join(\"\")\n .slice(0, 2)\n .toUpperCase();\n\n return (\n <GlassCard\n treatment=\"insight\"\n role=\"button\"\n tabIndex={0}\n className=\"cursor-pointer p-5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n onClick={onClick}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onClick();\n }\n }}\n >\n <div className=\"flex items-center gap-3 mb-4\">\n {org.logoUrl ? (\n <img\n src={org.logoUrl}\n alt={org.name}\n className=\"h-10 w-10 rounded-xl object-cover\"\n />\n ) : (\n <div className=\"flex h-10 w-10 items-center justify-center rounded-xl bg-action-primary-bg/15 text-primary text-sm font-bold\">\n {initials}\n </div>\n )}\n <div className=\"min-w-0 flex-1\">\n <p className=\"font-semibold truncate text-text-primary\">{org.name}</p>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n {org.publishedApps ?? 0}{\" \"}\n {tr(\"devportal.appsPublished\", \"apps published\")}\n </p>\n </div>\n <ArrowRight size={15} className=\"text-text-muted shrink-0\" />\n </div>\n\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"flex items-center gap-2 rounded-lg bg-bg-muted px-3 py-2\">\n <Download size={12} className=\"text-text-muted shrink-0\" />\n <div>\n <p className=\"text-[11px] text-text-secondary leading-none mb-0.5\">\n {tr(\"devportal.installs\", \"Installs\")}\n </p>\n <p className=\"text-sm font-semibold tabular-nums\">\n {(org.totalInstalls || 0).toLocaleString()}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2 rounded-lg bg-bg-muted px-3 py-2\">\n <DollarSign size={12} className=\"text-text-muted shrink-0\" />\n <div>\n <p className=\"text-[11px] text-text-secondary leading-none mb-0.5\">\n {tr(\"devportal.revenue\", \"Revenue\")}\n </p>\n <p className=\"text-sm font-semibold tabular-nums\">\n ${(org.totalRevenue || 0).toLocaleString()}\n </p>\n </div>\n </div>\n </div>\n </GlassCard>\n );\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"DocumentationPage.d.ts","sourceRoot":"","sources":["../../../src/devportal/pages/DocumentationPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AA0ThC,eAAO,MAAM,iBAAiB,EAAE,EAgL/B,CAAC"}
1
+ {"version":3,"file":"DocumentationPage.d.ts","sourceRoot":"","sources":["../../../src/devportal/pages/DocumentationPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAwRhC,eAAO,MAAM,iBAAiB,EAAE,EAgL/B,CAAC"}