@burdenoff/microfe-devportal 2026.1031.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.
- package/dist/devportal/DevportalRoutes.d.ts.map +1 -1
- package/dist/devportal/DevportalRoutes.js +13 -9
- package/dist/devportal/DevportalRoutes.js.map +1 -1
- package/dist/devportal/components/OnboardingChecklist.d.ts +16 -0
- package/dist/devportal/components/OnboardingChecklist.d.ts.map +1 -0
- package/dist/devportal/components/OnboardingChecklist.js +151 -0
- package/dist/devportal/components/OnboardingChecklist.js.map +1 -0
- package/dist/devportal/constants/developerLinks.d.ts +47 -0
- package/dist/devportal/constants/developerLinks.d.ts.map +1 -0
- package/dist/devportal/constants/developerLinks.js +27 -0
- package/dist/devportal/constants/developerLinks.js.map +1 -0
- package/dist/devportal/hooks/useGraphQL.d.ts.map +1 -1
- package/dist/devportal/hooks/useGraphQL.js +2 -1
- package/dist/devportal/hooks/useGraphQL.js.map +1 -1
- package/dist/devportal/hooks/useProducts.js +1 -1
- package/dist/devportal/hooks/useProducts.js.map +1 -1
- package/dist/devportal/pages/DashboardPage.d.ts.map +1 -1
- package/dist/devportal/pages/DashboardPage.js +107 -105
- package/dist/devportal/pages/DashboardPage.js.map +1 -1
- package/dist/devportal/pages/DocumentationPage.d.ts.map +1 -1
- package/dist/devportal/pages/DocumentationPage.js +114 -148
- package/dist/devportal/pages/DocumentationPage.js.map +1 -1
- package/dist/devportal/pages/PublisherProfilePage.d.ts.map +1 -1
- package/dist/devportal/pages/PublisherProfilePage.js +170 -160
- package/dist/devportal/pages/PublisherProfilePage.js.map +1 -1
- package/dist/devportal/pages/ResourcesPage.js +2 -2
- package/dist/devportal/pages/ResourcesPage.js.map +1 -1
- package/dist/devportal/pages/SetupPage.d.ts.map +1 -1
- package/dist/devportal/pages/SetupPage.js +363 -218
- package/dist/devportal/pages/SetupPage.js.map +1 -1
- package/dist/devportal/utils/developerOnboarding.d.ts +85 -0
- package/dist/devportal/utils/developerOnboarding.d.ts.map +1 -0
- package/dist/devportal/utils/developerOnboarding.js +104 -0
- package/dist/devportal/utils/developerOnboarding.js.map +1 -0
- package/dist/generated/global-operations.d.ts +92 -39
- package/dist/generated/global-operations.d.ts.map +1 -1
- package/dist/generated/global-operations.js +177 -141
- package/dist/generated/global-operations.js.map +1 -1
- package/dist/translations/en.d.ts +50 -0
- package/dist/translations/en.d.ts.map +1 -1
- package/dist/translations/en.js +51 -1
- package/dist/translations/en.js.map +1 -1
- 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":"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"}
|
|
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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"}
|
|
@@ -3,7 +3,7 @@ import { useListProductsForSelectorQuery as e } from "../../generated/global-ope
|
|
|
3
3
|
function t() {
|
|
4
4
|
let { data: t, loading: n, error: r } = e({ fetchPolicy: "cache-and-network" });
|
|
5
5
|
return {
|
|
6
|
-
products: t?.
|
|
6
|
+
products: t?.publicProducts.items ?? [],
|
|
7
7
|
isLoading: n,
|
|
8
8
|
error: r
|
|
9
9
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useProducts.js","names":[],"sources":["../../../src/devportal/hooks/useProducts.ts"],"sourcesContent":["/**\n * Shared hook for fetching the active platform products list — backs the\n * Compatible Products selector on the node/workflow create and edit forms,\n * so publishers pick from every real platform product instead of a\n * hardcoded handful that goes stale as new products launch.\n */\nimport { useListProductsForSelectorQuery } from \"../../generated/global-operations\";\n\nexport interface ProductOption {\n id: string;\n /**\n * The slug is what actually gets stored in StoreProduct.compatibleProducts\n * and what browseStore/homeGroupedProducts match against — never the\n * catalog row's `id`, which is a different UUID.\n */\n slug: string;\n name: string;\n}\n\ninterface UseProductsResult {\n products: ProductOption[];\n isLoading: boolean;\n error: Error | undefined;\n}\n\nexport function useProducts(): UseProductsResult {\n const { data, loading, error } = useListProductsForSelectorQuery({\n fetchPolicy: \"cache-and-network\",\n });\n\n return {\n products: data?.
|
|
1
|
+
{"version":3,"file":"useProducts.js","names":[],"sources":["../../../src/devportal/hooks/useProducts.ts"],"sourcesContent":["/**\n * Shared hook for fetching the active platform products list — backs the\n * Compatible Products selector on the node/workflow create and edit forms,\n * so publishers pick from every real platform product instead of a\n * hardcoded handful that goes stale as new products launch.\n */\nimport { useListProductsForSelectorQuery } from \"../../generated/global-operations\";\n\nexport interface ProductOption {\n id: string;\n /**\n * The slug is what actually gets stored in StoreProduct.compatibleProducts\n * and what browseStore/homeGroupedProducts match against — never the\n * catalog row's `id`, which is a different UUID.\n */\n slug: string;\n name: string;\n}\n\ninterface UseProductsResult {\n products: ProductOption[];\n isLoading: boolean;\n error: Error | undefined;\n}\n\nexport function useProducts(): UseProductsResult {\n const { data, loading, error } = useListProductsForSelectorQuery({\n fetchPolicy: \"cache-and-network\",\n });\n\n return {\n products: data?.publicProducts.items ?? [],\n isLoading: loading,\n error: error as Error | undefined,\n };\n}\n"],"mappings":";;AAyBA,SAAgB,IAAiC;CAC/C,IAAM,EAAE,SAAM,YAAS,aAAU,EAAgC,EAC/D,aAAa,qBACd,CAAC;AAEF,QAAO;EACL,UAAU,GAAM,eAAe,SAAS,EAAE;EAC1C,WAAW;EACJ;EACR"}
|
|
@@ -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;
|
|
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
|
|
11
|
-
import { AppWindow as
|
|
12
|
-
import { Fragment as
|
|
13
|
-
import { GlassCard as
|
|
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
|
|
16
|
-
let { navigate:
|
|
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
|
-
}),
|
|
20
|
-
if (
|
|
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
|
-
|
|
23
|
-
|
|
23
|
+
c(() => {
|
|
24
|
+
k && k !== y && x(k);
|
|
24
25
|
}, [
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
k,
|
|
27
|
+
y,
|
|
28
|
+
x
|
|
28
29
|
]);
|
|
29
|
-
let { data:
|
|
30
|
-
variables: { organizationId:
|
|
31
|
-
skip: !
|
|
30
|
+
let { data: A, loading: j } = r({
|
|
31
|
+
variables: { organizationId: k },
|
|
32
|
+
skip: !k,
|
|
32
33
|
fetchPolicy: "cache-and-network"
|
|
33
|
-
}),
|
|
34
|
-
|
|
35
|
-
if (!(
|
|
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
|
-
|
|
42
|
+
f?.(`${p}/setup`);
|
|
42
43
|
}
|
|
43
44
|
}, [
|
|
45
|
+
D,
|
|
44
46
|
E,
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
f
|
|
47
|
+
O.length,
|
|
48
|
+
f,
|
|
49
|
+
p
|
|
49
50
|
]);
|
|
50
|
-
let
|
|
51
|
-
|
|
52
|
-
},
|
|
53
|
-
return /* @__PURE__ */
|
|
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__ */
|
|
56
|
-
title: /* @__PURE__ */
|
|
57
|
-
|
|
56
|
+
children: /* @__PURE__ */ _(i, {
|
|
57
|
+
title: /* @__PURE__ */ v(g, { children: [
|
|
58
|
+
T("devportal.developer", "Developer"),
|
|
58
59
|
" ",
|
|
59
|
-
/* @__PURE__ */
|
|
60
|
+
/* @__PURE__ */ _(b, { children: T("devportal.portal", "Portal") })
|
|
60
61
|
] }),
|
|
61
|
-
actions:
|
|
62
|
+
actions: z ? /* @__PURE__ */ v("button", {
|
|
62
63
|
"data-tour": "devportal-dashboard-create-app-button",
|
|
63
|
-
onClick: () =>
|
|
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__ */
|
|
66
|
-
}) :
|
|
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: () =>
|
|
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__ */
|
|
71
|
+
children: [/* @__PURE__ */ _(h, { size: 16 }), T("devportal.createOrganization", "Create Organization")]
|
|
71
72
|
}),
|
|
72
|
-
children: /* @__PURE__ */
|
|
73
|
+
children: /* @__PURE__ */ v("div", {
|
|
73
74
|
className: "flex flex-col gap-8",
|
|
74
75
|
children: [
|
|
75
|
-
/* @__PURE__ */
|
|
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:
|
|
78
|
-
/* @__PURE__ */
|
|
79
|
-
icon: /* @__PURE__ */
|
|
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:
|
|
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__ */
|
|
92
|
-
title:
|
|
93
|
-
action:
|
|
94
|
-
onClick: () =>
|
|
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: [
|
|
98
|
+
children: [T("devportal.viewAll", "View all"), /* @__PURE__ */ _(u, { size: 14 })]
|
|
97
99
|
}) : void 0,
|
|
98
|
-
children:
|
|
100
|
+
children: R ? /* @__PURE__ */ _("div", {
|
|
99
101
|
className: "flex items-center justify-center py-12",
|
|
100
|
-
children: /* @__PURE__ */
|
|
101
|
-
}) :
|
|
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:
|
|
105
|
+
children: P.slice(0, 3).map((e) => /* @__PURE__ */ _(a, {
|
|
104
106
|
app: e,
|
|
105
|
-
onClick: () =>
|
|
107
|
+
onClick: () => L(`/apps/${e.id}`),
|
|
106
108
|
showActions: !1
|
|
107
109
|
}, e.id))
|
|
108
|
-
}) : /* @__PURE__ */
|
|
110
|
+
}) : /* @__PURE__ */ _(n, {
|
|
109
111
|
illustration: "empty-generic",
|
|
110
|
-
title:
|
|
111
|
-
description:
|
|
112
|
-
action:
|
|
113
|
-
onClick: () =>
|
|
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__ */
|
|
116
|
-
}) : /* @__PURE__ */
|
|
117
|
-
onClick: () =>
|
|
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__ */
|
|
121
|
+
children: [/* @__PURE__ */ _(h, { size: 16 }), T("devportal.createOrganization", "Create Organization")]
|
|
120
122
|
})
|
|
121
123
|
})
|
|
122
124
|
}),
|
|
123
|
-
(
|
|
124
|
-
title:
|
|
125
|
-
action:
|
|
126
|
-
onClick: () =>
|
|
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: [
|
|
130
|
+
children: [T("devportal.viewAll", "View all"), /* @__PURE__ */ _(u, { size: 14 })]
|
|
129
131
|
}) : void 0,
|
|
130
|
-
children:
|
|
132
|
+
children: R ? /* @__PURE__ */ _("div", {
|
|
131
133
|
className: "flex items-center justify-center py-12",
|
|
132
|
-
children: /* @__PURE__ */
|
|
133
|
-
}) : /* @__PURE__ */
|
|
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:
|
|
137
|
+
children: O.slice(0, 3).map((e) => /* @__PURE__ */ _(w, {
|
|
136
138
|
org: e,
|
|
137
|
-
onClick: () =>
|
|
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
|
-
},
|
|
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__ */
|
|
150
|
+
/* @__PURE__ */ _("span", {
|
|
149
151
|
className: "text-text-muted",
|
|
150
152
|
children: e
|
|
151
153
|
}),
|
|
152
|
-
/* @__PURE__ */
|
|
154
|
+
/* @__PURE__ */ _("span", {
|
|
153
155
|
className: "text-sm font-semibold tabular-nums",
|
|
154
156
|
children: t
|
|
155
157
|
}),
|
|
156
|
-
/* @__PURE__ */
|
|
158
|
+
/* @__PURE__ */ _("span", {
|
|
157
159
|
className: "text-sm text-text-secondary",
|
|
158
160
|
children: n
|
|
159
161
|
})
|
|
160
162
|
]
|
|
161
|
-
}),
|
|
163
|
+
}), C = ({ title: e, action: t, children: n }) => /* @__PURE__ */ v("section", {
|
|
162
164
|
className: "flex flex-col gap-4",
|
|
163
|
-
children: [/* @__PURE__ */
|
|
165
|
+
children: [/* @__PURE__ */ v("div", {
|
|
164
166
|
className: "flex items-center justify-between",
|
|
165
|
-
children: [/* @__PURE__ */
|
|
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
|
-
}),
|
|
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__ */
|
|
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__ */
|
|
183
|
+
children: [/* @__PURE__ */ v("div", {
|
|
182
184
|
className: "flex items-center gap-3 mb-4",
|
|
183
185
|
children: [
|
|
184
|
-
e.logoUrl ? /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
194
|
+
/* @__PURE__ */ v("div", {
|
|
193
195
|
className: "min-w-0 flex-1",
|
|
194
|
-
children: [/* @__PURE__ */
|
|
196
|
+
children: [/* @__PURE__ */ _("p", {
|
|
195
197
|
className: "font-semibold truncate text-text-primary",
|
|
196
198
|
children: e.name
|
|
197
|
-
}), /* @__PURE__ */
|
|
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__ */
|
|
208
|
+
/* @__PURE__ */ _(u, {
|
|
207
209
|
size: 15,
|
|
208
210
|
className: "text-text-muted shrink-0"
|
|
209
211
|
})
|
|
210
212
|
]
|
|
211
|
-
}), /* @__PURE__ */
|
|
213
|
+
}), /* @__PURE__ */ v("div", {
|
|
212
214
|
className: "grid grid-cols-2 gap-3",
|
|
213
|
-
children: [/* @__PURE__ */
|
|
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__ */
|
|
217
|
+
children: [/* @__PURE__ */ _(p, {
|
|
216
218
|
size: 12,
|
|
217
219
|
className: "text-text-muted shrink-0"
|
|
218
|
-
}), /* @__PURE__ */
|
|
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__ */
|
|
223
|
+
}), /* @__PURE__ */ _("p", {
|
|
222
224
|
className: "text-sm font-semibold tabular-nums",
|
|
223
225
|
children: (e.totalInstalls || 0).toLocaleString()
|
|
224
226
|
})] })]
|
|
225
|
-
}), /* @__PURE__ */
|
|
227
|
+
}), /* @__PURE__ */ v("div", {
|
|
226
228
|
className: "flex items-center gap-2 rounded-lg bg-bg-muted px-3 py-2",
|
|
227
|
-
children: [/* @__PURE__ */
|
|
229
|
+
children: [/* @__PURE__ */ _(f, {
|
|
228
230
|
size: 12,
|
|
229
231
|
className: "text-text-muted shrink-0"
|
|
230
|
-
}), /* @__PURE__ */
|
|
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__ */
|
|
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 {
|
|
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":";;;;;;;;;;;;;;AAyBA,IAAa,UAA0B;CACrC,IAAM,EAAE,aAAU,aAAU,gBAAa,sBAAmB,GAAc,EACpE,IAAK,GAAO,EAIZ,EAAE,MAAM,GAAU,SAAS,MAC/B,EAAuC;EACrC,WAAW,EAAE;EACb,aAAa;EACd,CAAC,EAKE,KACJ,GAAU,4BAA4B,EAAE,EACxC,QAAQ,MAAoC,EAAI,SAAS,WAAW,EAEhE,WAA2C;AAC1C,QAAc,OAGnB,QAFI,KAAe,EAAc,MAAM,MAAM,EAAE,OAAO,EAAY,GACzD,IACF,EAAc,IAAI;KACvB;AAEJ,SAAgB;AACd,EAAI,KAAiB,MAAkB,KACrC,EAAe,EAAc;IAE9B;EAAC;EAAe;EAAa;EAAe,CAAC;CAEhD,IAAM,EAAE,MAAM,GAAU,SAAS,MAC/B,EAAuC;EACrC,WAAW,EAAE,gBAAgB,GAAe;EAC5C,MAAM,CAAC;EACP,aAAa;EACd,CAAC,EAEE,IAAU,KAAe,GACzB,IAAO,KAAY;AAEzB,SAAgB;AACV,aAAe,CAAC,MAChB,IAAc,SAAS,IAC3B;OAAI;AACF,QAAI,aAAa,QAAQ,4BAA4B,CAAE;WACjD;AACN;;AAEF,OAAW,GAAG,EAAS,QAAQ;;IAC9B;EAAC;EAAa;EAAU,EAAc;EAAQ;EAAU;EAAS,CAAC;CAErE,IAAM,IACJ,GAAU,cAAc,OAAO,KAC5B,MAA2C,EAAE,KAC/C,IAAI,EAAE,EAEH,IAAY,GAAM,cAAc,cAAc,GAC9C,IAAY,EAAc,QAE1B,KAAkB,MAAiB;AACvC,MAAW,GAAG,IAAW,IAAO;IAG5B,IAAY,GAGZ,IAAU,CAAC,KAAe,EAAc,SAAS;AAEvD,QACE,kBAAC,GAAD;EAAiB,UAAA;YACf,kBAAC,GAAD;GACE,OACE,kBAAA,GAAA,EAAA,UAAA;IACG,EAAG,uBAAuB,YAAY;IAAE;IACzC,kBAAC,GAAD,EAAA,UAAe,EAAG,oBAAoB,SAAS,EAAgB,CAAA;IAC9D,EAAA,CAAA;GAEL,SACE,IACE,kBAAC,UAAD;IACE,aAAU;IACV,eAAe,EAAe,YAAY;IAC1C,WAAU;cAHZ,CAKE,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EACjB,EAAG,uBAAuB,aAAa,CACjC;QACN,IASD,OARF,kBAAC,UAAD;IACE,aAAU;IACV,eAAe,EAAe,qBAAqB;IACnD,WAAU;cAHZ,CAKE,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EACjB,EAAG,gCAAgC,sBAAsB,CACnD;;aAIb,kBAAC,OAAD;IAAK,WAAU;cAAf;KAEE,kBAAC,OAAD;MAAK,WAAU;gBACZ,IACC,kBAAC,GAAD,EAAS,WAAU,4CAA6C,CAAA,GAEhE,kBAAA,GAAA,EAAA,UAAA;OACE,kBAAC,GAAD;QACE,MAAM,kBAAC,GAAD,EAAW,MAAM,IAAM,CAAA;QAC7B,OAAO;QACP,OAAO,EAAG,kBAAkB,OAAO;QACnC,CAAA;OACF,kBAAC,OAAD,EAAK,WAAU,2BAA4B,CAAA;OAC3C,kBAAC,GAAD;QACE,MAAM,kBAAC,GAAD,EAAW,MAAM,IAAM,CAAA;QAC7B,OAAO;QACP,OAAO,EAAG,2BAA2B,gBAAgB;QACrD,CAAA;OACD,EAAA,CAAA;MAED,CAAA;KAGN,kBAAC,GAAD;MACE,OAAO,EAAG,gCAAgC,sBAAsB;MAChE,QACE,EAAa,SAAS,IACpB,kBAAC,UAAD;OACE,eAAe,EAAe,QAAQ;OACtC,WAAU;iBAFZ,CAIG,EAAG,qBAAqB,WAAW,EACpC,kBAAC,GAAD,EAAY,MAAM,IAAM,CAAA,CACjB;WACP,KAAA;gBAGL,IACC,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD,EAAS,WAAU,4CAA6C,CAAA;OAC5D,CAAA,GACJ,EAAa,SAAS,IACxB,kBAAC,OAAD;OAAK,WAAU;iBACZ,EACE,MAAM,GAAG,EAAE,CACX,KAAK,MACJ,kBAAC,GAAD;QAEO;QACL,eAAe,EAAe,SAAS,EAAI,KAAK;QAChD,aAAa;QACb,EAJK,EAAI,GAIT,CACF;OACA,CAAA,GAEN,kBAAC,GAAD;OACE,cAAa;OACb,OAAO,EAAG,+BAA+B,sBAAsB;OAC/D,aACE,IACI,EACE,gDACA,+CACD,GACD,EACE,yCACA,wDACD;OAEP,QACE,IACE,kBAAC,UAAD;QACE,eAAe,EAAe,YAAY;QAC1C,WAAU;kBAFZ,CAIE,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EACjB,EAAG,uBAAuB,aAAa,CACjC;YAET,kBAAC,UAAD;QACE,eAAe,EAAe,qBAAqB;QACnD,WAAU;kBAFZ,CAIE,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EACjB,EACC,gCACA,sBACD,CACM;;OAGb,CAAA;MAEQ,CAAA;MAGZ,KAAa,EAAc,SAAS,MACpC,kBAAC,GAAD;MACE,OAAO,EAAG,+BAA+B,qBAAqB;MAC9D,QACE,EAAc,SAAS,IACrB,kBAAC,UAAD;OACE,eAAe,EAAe,iBAAiB;OAC/C,WAAU;iBAFZ,CAIG,EAAG,qBAAqB,WAAW,EACpC,kBAAC,GAAD,EAAY,MAAM,IAAM,CAAA,CACjB;WACP,KAAA;gBAGL,IACC,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD,EAAS,WAAU,4CAA6C,CAAA;OAC5D,CAAA,GAEN,kBAAC,OAAD;OAAK,WAAU;iBACZ,EACE,MAAM,GAAG,EAAE,CACX,KAAK,MACJ,kBAAC,GAAD;QAEO;QACL,eACE,EAAe,kBAAkB,EAAI,KAAK;QAE5C,EALK,EAAI,GAKT,CACF;OACA,CAAA;MAEI,CAAA;KAEZ;;GACK,CAAA;EACG,CAAA;GAIhB,KAAmE,EACvE,SACA,UACA,eAEA,kBAAC,OAAD;CAAK,WAAU;WAAf;EACE,kBAAC,QAAD;GAAM,WAAU;aAAmB;GAAY,CAAA;EAC/C,kBAAC,QAAD;GAAM,WAAU;aAAsC;GAAa,CAAA;EACnE,kBAAC,QAAD;GAAM,WAAU;aAA+B;GAAa,CAAA;EACxD;IAGF,KAIA,EAAE,UAAO,WAAQ,kBACrB,kBAAC,WAAD;CAAS,WAAU;WAAnB,CACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,MAAD;GAAI,WAAU;aACX;GACE,CAAA,EACJ,EACG;KACL,EACO;IAGN,KAAoE,EACxE,QACA,iBACI;CACJ,IAAM,IAAK,GAAO,EAEZ,IAAW,EAAI,KAClB,MAAM,IAAI,CACV,KAAK,MAAc,EAAE,GAAG,CACxB,KAAK,GAAG,CACR,MAAM,GAAG,EAAE,CACX,aAAa;AAEhB,QACE,kBAAC,GAAD;EACE,WAAU;EACV,MAAK;EACL,UAAU;EACV,WAAU;EACD;EACT,YAAY,MAAM;AAChB,IAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,SACjC,EAAE,gBAAgB,EAClB,GAAS;;YATf,CAaE,kBAAC,OAAD;GAAK,WAAU;aAAf;IACG,EAAI,UACH,kBAAC,OAAD;KACE,KAAK,EAAI;KACT,KAAK,EAAI;KACT,WAAU;KACV,CAAA,GAEF,kBAAC,OAAD;KAAK,WAAU;eACZ;KACG,CAAA;IAER,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,KAAD;MAAG,WAAU;gBAA4C,EAAI;MAAS,CAAA,EACtE,kBAAC,KAAD;MAAG,WAAU;gBAAb;OACG,EAAI,iBAAiB;OAAG;OACxB,EAAG,2BAA2B,iBAAiB;OAC9C;QACA;;IACN,kBAAC,GAAD;KAAY,MAAM;KAAI,WAAU;KAA6B,CAAA;IACzD;MAEN,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAU,MAAM;KAAI,WAAU;KAA6B,CAAA,EAC3D,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;KAAG,WAAU;eACV,EAAG,sBAAsB,WAAW;KACnC,CAAA,EACJ,kBAAC,KAAD;KAAG,WAAU;gBACT,EAAI,iBAAiB,GAAG,gBAAgB;KACxC,CAAA,CACA,EAAA,CAAA,CACF;OACN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAY,MAAM;KAAI,WAAU;KAA6B,CAAA,EAC7D,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;KAAG,WAAU;eACV,EAAG,qBAAqB,UAAU;KACjC,CAAA,EACJ,kBAAC,KAAD;KAAG,WAAU;eAAb,CAAkD,MAC7C,EAAI,gBAAgB,GAAG,gBAAgB,CACxC;OACA,EAAA,CAAA,CACF;MACF;KACI"}
|
|
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":";;;;;;;;;;;;;;;AA0BA,IAAa,UAA0B;CACrC,IAAM,EAAE,aAAU,aAAU,gBAAa,sBAAmB,GAAc,EACpE,IAAK,GAAO,EAIZ,EAAE,MAAM,GAAU,SAAS,MAC/B,EAAuC;EACrC,WAAW,EAAE;EACb,aAAa;EACd,CAAC,EAKE,KACJ,GAAU,4BAA4B,EAAE,EACxC,QAAQ,MAAoC,EAAI,SAAS,WAAW,EAEhE,WAA2C;AAC1C,QAAc,OAGnB,QAFI,KAAe,EAAc,MAAM,MAAM,EAAE,OAAO,EAAY,GACzD,IACF,EAAc,IAAI;KACvB;AAEJ,SAAgB;AACd,EAAI,KAAiB,MAAkB,KACrC,EAAe,EAAc;IAE9B;EAAC;EAAe;EAAa;EAAe,CAAC;CAEhD,IAAM,EAAE,MAAM,GAAU,SAAS,MAC/B,EAAuC;EACrC,WAAW,EAAE,gBAAgB,GAAe;EAC5C,MAAM,CAAC;EACP,aAAa;EACd,CAAC,EAEE,IAAU,KAAe,GACzB,IAAO,KAAY;AAEzB,SAAgB;AACV,aAAe,CAAC,MAChB,IAAc,SAAS,IAC3B;OAAI;AACF,QAAI,aAAa,QAAQ,4BAA4B,CAAE;WACjD;AACN;;AAEF,OAAW,GAAG,EAAS,QAAQ;;IAC9B;EAAC;EAAa;EAAU,EAAc;EAAQ;EAAU;EAAS,CAAC;CAErE,IAAM,IACJ,GAAU,cAAc,OAAO,KAC5B,MAA2C,EAAE,KAC/C,IAAI,EAAE,EAEH,IAAY,GAAM,cAAc,cAAc,GAC9C,IAAY,EAAc,QAE1B,KAAkB,MAAiB;AACvC,MAAW,GAAG,IAAW,IAAO;IAG5B,IAAY,GAGZ,IAAU,CAAC,KAAe,EAAc,SAAS;AAEvD,QACE,kBAAC,GAAD;EAAiB,UAAA;YACf,kBAAC,GAAD;GACE,OACE,kBAAA,GAAA,EAAA,UAAA;IACG,EAAG,uBAAuB,YAAY;IAAE;IACzC,kBAAC,GAAD,EAAA,UAAe,EAAG,oBAAoB,SAAS,EAAgB,CAAA;IAC9D,EAAA,CAAA;GAEL,SACE,IACE,kBAAC,UAAD;IACE,aAAU;IACV,eAAe,EAAe,YAAY;IAC1C,WAAU;cAHZ,CAKE,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EACjB,EAAG,uBAAuB,aAAa,CACjC;QACN,IASD,OARF,kBAAC,UAAD;IACE,aAAU;IACV,eAAe,EAAe,qBAAqB;IACnD,WAAU;cAHZ,CAKE,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EACjB,EAAG,gCAAgC,sBAAsB,CACnD;;aAIb,kBAAC,OAAD;IAAK,WAAU;cAAf;KAEE,kBAAC,GAAD,EAAqB,YAAY,GAAkB,CAAA;KAGnD,kBAAC,OAAD;MAAK,WAAU;gBACZ,IACC,kBAAC,GAAD,EAAS,WAAU,4CAA6C,CAAA,GAEhE,kBAAA,GAAA,EAAA,UAAA;OACE,kBAAC,GAAD;QACE,MAAM,kBAAC,GAAD,EAAW,MAAM,IAAM,CAAA;QAC7B,OAAO;QACP,OAAO,EAAG,kBAAkB,OAAO;QACnC,CAAA;OACF,kBAAC,OAAD,EAAK,WAAU,2BAA4B,CAAA;OAC3C,kBAAC,GAAD;QACE,MAAM,kBAAC,GAAD,EAAW,MAAM,IAAM,CAAA;QAC7B,OAAO;QACP,OAAO,EAAG,2BAA2B,gBAAgB;QACrD,CAAA;OACD,EAAA,CAAA;MAED,CAAA;KAGN,kBAAC,GAAD;MACE,OAAO,EAAG,gCAAgC,sBAAsB;MAChE,QACE,EAAa,SAAS,IACpB,kBAAC,UAAD;OACE,eAAe,EAAe,QAAQ;OACtC,WAAU;iBAFZ,CAIG,EAAG,qBAAqB,WAAW,EACpC,kBAAC,GAAD,EAAY,MAAM,IAAM,CAAA,CACjB;WACP,KAAA;gBAGL,IACC,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD,EAAS,WAAU,4CAA6C,CAAA;OAC5D,CAAA,GACJ,EAAa,SAAS,IACxB,kBAAC,OAAD;OAAK,WAAU;iBACZ,EACE,MAAM,GAAG,EAAE,CACX,KAAK,MACJ,kBAAC,GAAD;QAEO;QACL,eAAe,EAAe,SAAS,EAAI,KAAK;QAChD,aAAa;QACb,EAJK,EAAI,GAIT,CACF;OACA,CAAA,GAEN,kBAAC,GAAD;OACE,cAAa;OACb,OAAO,EAAG,+BAA+B,sBAAsB;OAC/D,aACE,IACI,EACE,gDACA,+CACD,GACD,EACE,yCACA,wDACD;OAEP,QACE,IACE,kBAAC,UAAD;QACE,eAAe,EAAe,YAAY;QAC1C,WAAU;kBAFZ,CAIE,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EACjB,EAAG,uBAAuB,aAAa,CACjC;YAET,kBAAC,UAAD;QACE,eAAe,EAAe,qBAAqB;QACnD,WAAU;kBAFZ,CAIE,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EACjB,EACC,gCACA,sBACD,CACM;;OAGb,CAAA;MAEQ,CAAA;MAGZ,KAAa,EAAc,SAAS,MACpC,kBAAC,GAAD;MACE,OAAO,EAAG,+BAA+B,qBAAqB;MAC9D,QACE,EAAc,SAAS,IACrB,kBAAC,UAAD;OACE,eAAe,EAAe,iBAAiB;OAC/C,WAAU;iBAFZ,CAIG,EAAG,qBAAqB,WAAW,EACpC,kBAAC,GAAD,EAAY,MAAM,IAAM,CAAA,CACjB;WACP,KAAA;gBAGL,IACC,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD,EAAS,WAAU,4CAA6C,CAAA;OAC5D,CAAA,GAEN,kBAAC,OAAD;OAAK,WAAU;iBACZ,EACE,MAAM,GAAG,EAAE,CACX,KAAK,MACJ,kBAAC,GAAD;QAEO;QACL,eACE,EAAe,kBAAkB,EAAI,KAAK;QAE5C,EALK,EAAI,GAKT,CACF;OACA,CAAA;MAEI,CAAA;KAEZ;;GACK,CAAA;EACG,CAAA;GAIhB,KAAmE,EACvE,SACA,UACA,eAEA,kBAAC,OAAD;CAAK,WAAU;WAAf;EACE,kBAAC,QAAD;GAAM,WAAU;aAAmB;GAAY,CAAA;EAC/C,kBAAC,QAAD;GAAM,WAAU;aAAsC;GAAa,CAAA;EACnE,kBAAC,QAAD;GAAM,WAAU;aAA+B;GAAa,CAAA;EACxD;IAGF,KAIA,EAAE,UAAO,WAAQ,kBACrB,kBAAC,WAAD;CAAS,WAAU;WAAnB,CACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,MAAD;GAAI,WAAU;aACX;GACE,CAAA,EACJ,EACG;KACL,EACO;IAGN,KAAoE,EACxE,QACA,iBACI;CACJ,IAAM,IAAK,GAAO,EAEZ,IAAW,EAAI,KAClB,MAAM,IAAI,CACV,KAAK,MAAc,EAAE,GAAG,CACxB,KAAK,GAAG,CACR,MAAM,GAAG,EAAE,CACX,aAAa;AAEhB,QACE,kBAAC,GAAD;EACE,WAAU;EACV,MAAK;EACL,UAAU;EACV,WAAU;EACD;EACT,YAAY,MAAM;AAChB,IAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,SACjC,EAAE,gBAAgB,EAClB,GAAS;;YATf,CAaE,kBAAC,OAAD;GAAK,WAAU;aAAf;IACG,EAAI,UACH,kBAAC,OAAD;KACE,KAAK,EAAI;KACT,KAAK,EAAI;KACT,WAAU;KACV,CAAA,GAEF,kBAAC,OAAD;KAAK,WAAU;eACZ;KACG,CAAA;IAER,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,KAAD;MAAG,WAAU;gBAA4C,EAAI;MAAS,CAAA,EACtE,kBAAC,KAAD;MAAG,WAAU;gBAAb;OACG,EAAI,iBAAiB;OAAG;OACxB,EAAG,2BAA2B,iBAAiB;OAC9C;QACA;;IACN,kBAAC,GAAD;KAAY,MAAM;KAAI,WAAU;KAA6B,CAAA;IACzD;MAEN,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAU,MAAM;KAAI,WAAU;KAA6B,CAAA,EAC3D,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;KAAG,WAAU;eACV,EAAG,sBAAsB,WAAW;KACnC,CAAA,EACJ,kBAAC,KAAD;KAAG,WAAU;gBACT,EAAI,iBAAiB,GAAG,gBAAgB;KACxC,CAAA,CACA,EAAA,CAAA,CACF;OACN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAY,MAAM;KAAI,WAAU;KAA6B,CAAA,EAC7D,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;KAAG,WAAU;eACV,EAAG,qBAAqB,UAAU;KACjC,CAAA,EACJ,kBAAC,KAAD;KAAG,WAAU;eAAb,CAAkD,MAC7C,EAAI,gBAAgB,GAAG,gBAAgB,CACxC;OACA,EAAA,CAAA,CACF;MACF;KACI"}
|
|
@@ -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;
|
|
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"}
|