11import React , { useEffect , useState } from 'react' ;
22import { T , useTranslate } from '@tolgee/react' ;
3- import { Alert , Box , styled , Typography } from '@mui/material' ;
3+ import {
4+ Alert ,
5+ Box ,
6+ Checkbox ,
7+ FormControlLabel ,
8+ styled ,
9+ Typography ,
10+ } from '@mui/material' ;
411
512import { DashboardPage } from 'tg.component/layout/DashboardPage' ;
613import { CompactView } from 'tg.component/layout/CompactView' ;
@@ -23,11 +30,9 @@ type ConsentInfo = {
2330 allProjects : boolean ;
2431} ;
2532
26- const StyledCapabilities = styled ( 'ul' ) `
27- margin: 0;
28- padding-left: 20px;
33+ const StyledCapabilities = styled ( 'div' ) `
2934 display: grid;
30- gap: 8px ;
35+ gap: 4px ;
3136` ;
3237
3338const StyledButtons = styled ( Box ) `
@@ -45,19 +50,27 @@ const OAuth2ConsentView: React.FC<React.PropsWithChildren<unknown>> = () => {
4550 const scope = asString ( search . scope ) ;
4651 const state = asString ( search . state ) ;
4752 const [ info , setInfo ] = useState < ConsentInfo > ( ) ;
53+ const [ selectedScopes , setSelectedScopes ] = useState < string [ ] > ( [ ] ) ;
4854 const [ failed , setFailed ] = useState ( false ) ;
4955 const [ submitting , setSubmitting ] = useState ( false ) ;
5056
5157 useEffect ( ( ) => {
5258 apiV2HttpService
5359 . get < ConsentInfo > ( 'oauth2/consent-info' , { clientId, scope, state } )
54- . then ( setInfo )
60+ . then ( ( data ) => {
61+ setInfo ( data ) ;
62+ setSelectedScopes ( data . scopes ) ;
63+ } )
5564 . catch ( ( ) => setFailed ( true ) ) ;
5665 } , [ clientId , scope , state ] ) ;
5766
58- // Consent is submitted as a real form POST to the authorization endpoint so the browser sends the
59- // session cookie and follows the redirect back to the client. Approving = posting the scopes; denying
60- // = posting none, which the server turns into access_denied.
67+ const toggleScope = ( s : string ) => {
68+ setSelectedScopes ( ( prev ) =>
69+ prev . includes ( s ) ? prev . filter ( ( x ) => x !== s ) : [ ...prev , s ]
70+ ) ;
71+ } ;
72+
73+ // Real form POST (not fetch) so the browser sends the session cookie and follows the redirect back to the client.
6174 const submitConsent = ( approvedScopes : string [ ] ) => {
6275 setSubmitting ( true ) ;
6376 const form = document . createElement ( 'form' ) ;
@@ -117,9 +130,19 @@ const OAuth2ConsentView: React.FC<React.PropsWithChildren<unknown>> = () => {
117130 < Box data-cy = "oauth2-consent" >
118131 < StyledCapabilities >
119132 { info . scopes . map ( ( s ) => (
120- < li key = { s } data-cy = "oauth2-consent-scope" >
121- { getScopeTranslation ( s as PermissionModelScope ) }
122- </ li >
133+ < FormControlLabel
134+ key = { s }
135+ data-cy = "oauth2-consent-scope"
136+ data-cy-scope = { s }
137+ control = {
138+ < Checkbox
139+ size = "small"
140+ checked = { selectedScopes . includes ( s ) }
141+ onChange = { ( ) => toggleScope ( s ) }
142+ />
143+ }
144+ label = { getScopeTranslation ( s as PermissionModelScope ) }
145+ />
123146 ) ) }
124147 </ StyledCapabilities >
125148 { info . project && (
@@ -165,7 +188,8 @@ const OAuth2ConsentView: React.FC<React.PropsWithChildren<unknown>> = () => {
165188 color = "primary"
166189 data-cy = "oauth2-consent-allow"
167190 loading = { submitting }
168- onClick = { ( ) => submitConsent ( info . scopes ) }
191+ disabled = { selectedScopes . length === 0 }
192+ onClick = { ( ) => submitConsent ( selectedScopes ) }
169193 >
170194 < T keyName = "oauth2_consent_allow" defaultValue = "Allow" />
171195 </ LoadingButton >
0 commit comments