Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 63 additions & 54 deletions admin-ui/plugins/auth-server/components/Scopes/ScopeAddPage.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import React, { useEffect, useState, useMemo, useCallback } from 'react'
import React, { useEffect, useState, useCallback } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { useQueryClient } from '@tanstack/react-query'
import { CardBody, Card } from 'Components'
import ScopeForm from './ScopeForm'
import GluuLoader from 'Routes/Apps/Gluu/GluuLoader'
import { getAttributes, getScripts } from 'Redux/features/initSlice'
import { buildPayload } from 'Utils/PermChecker'
import GluuAlert from 'Routes/Apps/Gluu/GluuAlert'
import { updateToast } from 'Redux/features/toastSlice'
import { useTranslation } from 'react-i18next'
import applicationStyle from 'Routes/Apps/Gluu/styles/applicationstyle'
import { usePostOauthScopes } from 'JansConfigApi'
import { usePostOauthScopes, getGetOauthScopesQueryKey } from 'JansConfigApi'
import type { Scope } from 'JansConfigApi'
import type { ExtendedScope, ScopeScript, ScopeClaim, ModifiedFields } from './types'
import { EMPTY_SCOPE } from './types'
import type { ScopeScript, ScopeClaim, ModifiedFields } from './types'
import { useScopeActions } from './hooks'
import { ScopeWithMessage, INITIAL_SCOPE } from './constants'

interface InitState {
scripts: ScopeScript[]
Expand All @@ -25,17 +27,72 @@ interface RootState {

const ScopeAddPage: React.FC = () => {
const { t } = useTranslation()

const dispatch = useDispatch()
const { logScopeCreation, navigateToScopeList } = useScopeActions()
const queryClient = useQueryClient()

const scripts = useSelector((state: RootState) => state.initReducer.scripts)
const attributes = useSelector((state: RootState) => state.initReducer.attributes)

const { logScopeCreation, navigateToScopeList } = useScopeActions()

const [modifiedFields, setModifiedFields] = useState<ModifiedFields>({})
const [errorMessage, setErrorMessage] = useState<string | null>(null)

const createScope = usePostOauthScopes()

const handleSubmit = useCallback(
async (data: string) => {
if (!data) return

setErrorMessage(null)

let parsedData: ScopeWithMessage
try {
parsedData = JSON.parse(data) as ScopeWithMessage
} catch (error) {
console.error('Error parsing scope data:', error)
setErrorMessage(t('messages.error_in_parsing_data'))
return
}

try {
const message = parsedData.action_message || ''
delete parsedData.action_message

const response = await createScope.mutateAsync({ data: parsedData as Scope })

queryClient.invalidateQueries({
predicate: (query) => {
const queryKey = query.queryKey[0] as string
return (
queryKey === getGetOauthScopesQueryKey()[0] || queryKey === 'getOauthScopesByInum'
)
},
})

const successMessage =
response?.id || response?.displayName
? `Scope '${response.id || response.displayName}' created successfully`
: t('messages.scope_created_successfully')

dispatch(updateToast(true, 'success', successMessage))

try {
await logScopeCreation(parsedData as Scope, message, modifiedFields)
} catch (auditError) {
console.error('Error logging audit:', auditError)
}

navigateToScopeList()
} catch (error) {
console.error('Error creating scope:', error)
setErrorMessage(error instanceof Error ? error.message : t('messages.error_in_saving'))
}
},
[createScope, logScopeCreation, modifiedFields, navigateToScopeList, t, queryClient, dispatch],
)

useEffect(() => {
if (attributes.length === 0) {
const attributeOptions: Record<string, unknown> = {}
Expand All @@ -55,39 +112,6 @@ const ScopeAddPage: React.FC = () => {
}
}, [dispatch, attributes.length, scripts.length])

async function handleSubmit(data: string) {
if (!data) return

setErrorMessage(null)

let parsedData: Scope
try {
parsedData = JSON.parse(data) as Scope
} catch (error) {
console.error('Error parsing scope data:', error)
setErrorMessage(t('messages.error_in_parsing_data'))
return
}

try {
const message = (parsedData as Record<string, unknown>).action_message as string
delete (parsedData as Record<string, unknown>).action_message

await createScope.mutateAsync({ data: parsedData })

try {
await logScopeCreation(parsedData, message, modifiedFields)
} catch (auditError) {
console.error('Error logging audit:', auditError)
}

navigateToScopeList()
} catch (error) {
console.error('Error creating scope:', error)
setErrorMessage(error instanceof Error ? error.message : t('messages.error_in_saving'))
}
}

const handleSearch = useCallback(
(value: string) => {
dispatch({
Expand All @@ -98,21 +122,6 @@ const ScopeAddPage: React.FC = () => {
[dispatch],
)

const scope: ExtendedScope = useMemo(
() => ({
...EMPTY_SCOPE,
claims: [],
dynamicScopeScripts: [],
defaultScope: false,
attributes: {
spontaneousClientId: undefined,
spontaneousClientScopes: [],
showInConfigurationEndpoint: false,
},
}),
[],
)

return (
<GluuLoader blocking={createScope.isPending}>
<GluuAlert
Expand All @@ -123,7 +132,7 @@ const ScopeAddPage: React.FC = () => {
<Card className="mb-3" style={applicationStyle.mainCard}>
<CardBody>
<ScopeForm
scope={scope}
scope={INITIAL_SCOPE}
scripts={scripts}
attributes={attributes}
handleSubmit={handleSubmit}
Expand Down
176 changes: 89 additions & 87 deletions admin-ui/plugins/auth-server/components/Scopes/ScopeDetailPage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useContext } from 'react'
import React, { useContext, useMemo, useCallback } from 'react'
import { Container, Row, Col } from 'Components'
import GluuFormDetailRow from 'Routes/Apps/Gluu/GluuFormDetailRow'
import { SCOPE } from 'Utils/ApiResources'
Expand All @@ -7,98 +7,100 @@ import { ThemeContext } from 'Context/theme/themeContext'
import customColors from '@/customColors'
import type { ScopeDetailPageProps } from './types'

const CONTAINER_STYLES = { backgroundColor: customColors.whiteSmoke } as const

const ScopeDetailPage: React.FC<ScopeDetailPageProps> = ({ row }) => {
const { t } = useTranslation()

const theme = useContext(ThemeContext)
const selectedTheme = theme?.state?.theme || 'light'

function getBadgeTheme(status: boolean | undefined): string {
if (status) {
return `primary-${selectedTheme}`
} else {
return 'dimmed'
}
}
const selectedTheme = useMemo(() => theme?.state?.theme || 'light', [theme?.state?.theme])

const defaultScopeValue = useMemo(
() => (row.defaultScope ? t('options.yes') : t('options.no')),
[row.defaultScope, t],
)

const attributeKeys = useMemo(() => Object.keys(row.attributes || {}), [row.attributes])

const defaultScopeBadgeColor = useMemo(
() => (row.defaultScope ? `primary-${selectedTheme}` : 'dimmed'),
[row.defaultScope, selectedTheme],
)

const renderAttributeRow = useCallback(
(item: string, key: number) => (
<GluuFormDetailRow
key={key}
label={item}
isBadge={true}
value={String(row.attributes?.[item as keyof typeof row.attributes])}
doc_category={SCOPE}
doc_entry={`attributes.${item}`}
/>
),
[row.attributes],
)

return (
<React.Fragment>
<Container style={{ backgroundColor: customColors.whiteSmoke }}>
<Row>
<Col sm={6}>
<GluuFormDetailRow
label="fields.inum"
value={row.inum}
doc_category={SCOPE}
doc_entry="inum"
/>
</Col>
<Col sm={6}>
<GluuFormDetailRow
label="fields.id"
value={row.id}
doc_category={SCOPE}
doc_entry="id"
/>
</Col>
</Row>
<Row>
<Col sm={6}>
<GluuFormDetailRow
label="fields.description"
value={row.description}
doc_category={SCOPE}
doc_entry="description"
/>
</Col>
<Col sm={6}>
<GluuFormDetailRow
label="fields.displayname"
value={row.displayName}
doc_category={SCOPE}
doc_entry="displayName"
/>
</Col>
</Row>
<Row>
<Col sm={6}>
<GluuFormDetailRow
label="fields.scope_type"
value={row.scopeType}
doc_category={SCOPE}
doc_entry="scopeType"
isBadge
/>
</Col>
<Col sm={6}>
<GluuFormDetailRow
label="fields.default_scope"
isBadge
badgeColor={getBadgeTheme(row.defaultScope)}
value={row.defaultScope ? t('options.yes') : t('options.no')}
doc_category={SCOPE}
doc_entry="defaultScope"
/>
</Col>
</Row>
<Row>
<Col sm={3}>{t('fields.attributes')}:</Col>
<Col sm={9}>
{Object.keys(row.attributes || {}).map((item, key) => {
return (
<GluuFormDetailRow
key={key}
label={item}
isBadge={true}
value={String(row.attributes?.[item as keyof typeof row.attributes])}
doc_category={SCOPE}
doc_entry={`attributes.${item}`}
/>
)
})}
</Col>
</Row>
</Container>
</React.Fragment>
<Container style={CONTAINER_STYLES}>
<Row>
<Col sm={6}>
<GluuFormDetailRow
label="fields.inum"
value={row.inum}
doc_category={SCOPE}
doc_entry="inum"
/>
</Col>
<Col sm={6}>
<GluuFormDetailRow label="fields.id" value={row.id} doc_category={SCOPE} doc_entry="id" />
</Col>
</Row>
<Row>
<Col sm={6}>
<GluuFormDetailRow
label="fields.description"
value={row.description}
doc_category={SCOPE}
doc_entry="description"
/>
</Col>
<Col sm={6}>
<GluuFormDetailRow
label="fields.displayname"
value={row.displayName}
doc_category={SCOPE}
doc_entry="displayName"
/>
</Col>
</Row>
<Row>
<Col sm={6}>
<GluuFormDetailRow
label="fields.scope_type"
value={row.scopeType}
doc_category={SCOPE}
doc_entry="scopeType"
isBadge
/>
</Col>
<Col sm={6}>
<GluuFormDetailRow
label="fields.default_scope"
isBadge
badgeColor={defaultScopeBadgeColor}
value={defaultScopeValue}
doc_category={SCOPE}
doc_entry="defaultScope"
/>
</Col>
</Row>
<Row>
<Col sm={3}>{t('fields.attributes')}:</Col>
<Col sm={9}>{attributeKeys.map(renderAttributeRow)}</Col>
</Row>
</Container>
)
}

Expand Down
Loading
Loading