diff --git a/apps/console/src/pages/iam/auth/ConsentPage.tsx b/apps/console/src/pages/iam/auth/ConsentPage.tsx index cb6c314e8..6f3b150fe 100644 --- a/apps/console/src/pages/iam/auth/ConsentPage.tsx +++ b/apps/console/src/pages/iam/auth/ConsentPage.tsx @@ -18,6 +18,7 @@ import { useTranslate } from "@probo/i18n"; import { Button, IconArrowsClockwise, + IconChevronDown, IconEnvelope, IconKey, IconLockOpen, @@ -25,12 +26,13 @@ import { IconUserCircle, useToast, } from "@probo/ui"; -import { useCallback, useState } from "react"; +import { useCallback, useMemo, useState } from "react"; import { type PreloadedQuery, useMutation, usePreloadedQuery } from "react-relay"; import { graphql } from "relay-runtime"; import type { ConsentPageMutation } from "#/__generated__/iam/ConsentPageMutation.graphql"; import type { ConsentPageQuery } from "#/__generated__/iam/ConsentPageQuery.graphql"; +import { formatApiScopeLabel } from "#/pages/iam/oauthTokens/_components/scopeLabels"; export const consentPageQuery = graphql` query ConsentPageQuery($consentId: ID!) { @@ -74,7 +76,81 @@ function scopeIcon(name: string): React.ReactNode { } function scopeLabel(name: string): string { - return scopeLabels[name] ?? name; + return scopeLabels[name] ?? formatApiScopeLabel(name); +} + +function isApiScope(scope: string): boolean { + return scope.startsWith("v1:"); +} + +function partitionScopes(scopes: readonly string[]) { + const oidcScopes: string[] = []; + const apiScopes: string[] = []; + + for (const scope of scopes) { + if (isApiScope(scope)) { + apiScopes.push(scope); + } else { + oidcScopes.push(scope); + } + } + + return { oidcScopes, apiScopes }; +} + +function ConsentScopeRow(props: { + scope: string; + translate: (label: string) => string; + nested?: boolean; +}) { + const label = scopeLabel(props.scope); + const translated = label !== props.scope ? props.translate(label) : label; + + return ( +