From 29cc6d86b62e50ab9d24fc243af9b4b0c47574c5 Mon Sep 17 00:00:00 2001 From: faisalsiddique4400 Date: Wed, 17 Jun 2026 15:26:26 +0500 Subject: [PATCH 01/11] knip cleanup Signed-off-by: faisalsiddique4400 --- .../components/GluuTable/GluuTable.style.ts | 16 +++-- .../app/components/GluuTable/GluuTable.tsx | 67 ++++++++++--------- .../AgamaFlows/AgamaProjectConfigModal.tsx | 23 ++----- .../utils/formValidationUtils.test.ts | 38 ----------- .../user-management/common/Constants.ts | 22 +++--- .../components/UserEditPage.tsx | 11 +-- .../user-management/components/UserForm.tsx | 9 ++- .../user-management/utils/claimLabelUtils.ts | 5 ++ .../utils/formValidationUtils.ts | 18 ----- 9 files changed, 81 insertions(+), 128 deletions(-) diff --git a/admin-ui/app/components/GluuTable/GluuTable.style.ts b/admin-ui/app/components/GluuTable/GluuTable.style.ts index 27cd75717d..17f1e6c753 100644 --- a/admin-ui/app/components/GluuTable/GluuTable.style.ts +++ b/admin-ui/app/components/GluuTable/GluuTable.style.ts @@ -146,21 +146,22 @@ export const useStyles = makeStyles()(( headerCellActions: { textAlign: 'center', }, + headerCellSortable: { + padding: 0, + }, sortableHeader: { 'cursor': 'pointer', 'display': 'flex', 'alignItems': 'center', 'justifyContent': 'flex-start', - 'position': 'absolute', - 'left': 0, - 'top': 0, - 'right': 6, - 'bottom': 0, - 'width': 'auto', + 'width': '100%', 'padding': '14px 16px', + 'paddingRight': 20, + 'boxSizing': 'border-box', 'background': 'none', 'border': 'none', 'font': 'inherit', + 'lineHeight': '28px', 'color': headerColor, 'textAlign': 'inherit', '&:hover [data-sort-icon]': { @@ -178,7 +179,8 @@ export const useStyles = makeStyles()(( display: 'inline-flex', alignItems: 'center', opacity: OPACITY.NONE, - transition: 'opacity 0.15s ease', + transformOrigin: 'center', + transition: 'opacity 0.15s ease, transform 0.2s ease', }, cell: { padding: '14px 16px', diff --git a/admin-ui/app/components/GluuTable/GluuTable.tsx b/admin-ui/app/components/GluuTable/GluuTable.tsx index 7fe6f783d0..a73f64a46b 100644 --- a/admin-ui/app/components/GluuTable/GluuTable.tsx +++ b/admin-ui/app/components/GluuTable/GluuTable.tsx @@ -401,13 +401,9 @@ const GluuTable = (props: Readonly>) => { const handleSort = useCallback((columnKey: string) => { setSortState((prev) => { const isSameColumn = prev.column === columnKey - const nextDirection: SortDirection = !isSameColumn - ? 'asc' - : prev.direction === 'asc' - ? 'desc' - : null + const nextDirection: SortDirection = isSameColumn && prev.direction === 'asc' ? 'desc' : 'asc' return { - column: nextDirection ? columnKey : null, + column: columnKey, direction: nextDirection, } }) @@ -510,7 +506,11 @@ const GluuTable = (props: Readonly>) => { setHeaderCellRef(id, el)} key={`${id}-${colIdx}`} - className={`${classes.headerCell} ${classes.headerCellResizable}`} + className={ + isSortable + ? `${classes.headerCell} ${classes.headerCellSortable}` + : `${classes.headerCell} ${classes.headerCellResizable}` + } style={{ width: effectiveWidths[id], ...(parseMinWidth(col) != null && { minWidth: parseMinWidth(col) }), @@ -529,12 +529,17 @@ const GluuTable = (props: Readonly>) => { onClick={() => handleSort(id)} > {col.label} - - + + ) : ( @@ -542,23 +547,25 @@ const GluuTable = (props: Readonly>) => { {col.label} )} -
{ - e.preventDefault() - e.stopPropagation() - handleResizeStart(id, e.clientX, e.currentTarget) - }} - onTouchStart={(e) => { - e.preventDefault() - if (e.touches.length > 0) { - handleResizeStart(id, e.touches[0].clientX, e.currentTarget) - } - }} - /> + {(colIdx < columns.length - 1 || (actions?.length ?? 0) > 0) && ( +
{ + e.preventDefault() + e.stopPropagation() + handleResizeStart(id, e.clientX, e.currentTarget) + }} + onTouchStart={(e) => { + e.preventDefault() + if (e.touches.length > 0) { + handleResizeStart(id, e.touches[0].clientX, e.currentTarget) + } + }} + /> + )} ) })} diff --git a/admin-ui/plugins/auth-server/components/Authentication/AgamaFlows/AgamaProjectConfigModal.tsx b/admin-ui/plugins/auth-server/components/Authentication/AgamaFlows/AgamaProjectConfigModal.tsx index c8617e9530..bac909b1a9 100644 --- a/admin-ui/plugins/auth-server/components/Authentication/AgamaFlows/AgamaProjectConfigModal.tsx +++ b/admin-ui/plugins/auth-server/components/Authentication/AgamaFlows/AgamaProjectConfigModal.tsx @@ -3,8 +3,7 @@ import { createPortal } from 'react-dom' import { useTranslation } from 'react-i18next' import { Close } from '@/components/icons' import { Box, Divider } from '@mui/material' -import MaterialTable from '@material-table/core' -import type { Column } from '@material-table/core' +import { GluuTable, type ColumnDef } from '@/components/GluuTable' import { updateToast } from 'Redux/features/toastSlice' import isEmpty from 'lodash/isEmpty' import AceEditor from 'react-ace' @@ -228,13 +227,10 @@ const AgamaProjectConfigModal: React.FC = ({ save_data(JSON.stringify(projectConfigs)) } - const tableColumns: Column[] = useMemo( + const tableColumns: ColumnDef[] = useMemo( () => [ - { title: `${t('fields.flow')}`, field: 'flow' }, - { - title: `${t('fields.errors')}`, - field: 'error', - }, + { key: 'flow', label: t('fields.flow') }, + { key: 'error', label: t('fields.errors') }, ], [t], ) @@ -260,9 +256,6 @@ const AgamaProjectConfigModal: React.FC = ({ [handler], ) - const tableComponents = useMemo(() => ({ Toolbar: () => null }), []) - const tableOptions = useMemo(() => ({ search: false, selection: false, paging: false }), []) - if (!isOpen) return null return createPortal( @@ -374,13 +367,11 @@ const AgamaProjectConfigModal: React.FC = ({ {t('fields.errors')}: {projectDetailsData?.details?.error ?? 'No'} - columns={tableColumns} data={flowErrors} - isLoading={projectDetailsFetching} - title="" - options={tableOptions} + loading={projectDetailsFetching} + getRowKey={(row) => row.id} /> {projectConfigs ? ( diff --git a/admin-ui/plugins/user-management/__tests__/utils/formValidationUtils.test.ts b/admin-ui/plugins/user-management/__tests__/utils/formValidationUtils.test.ts index 39d4ab3331..9f97487bfa 100644 --- a/admin-ui/plugins/user-management/__tests__/utils/formValidationUtils.test.ts +++ b/admin-ui/plugins/user-management/__tests__/utils/formValidationUtils.test.ts @@ -1,7 +1,5 @@ import { isEmptyValue, - shouldDisableApplyButton, - hasFormChanges, buildFormOperations, diffFormValues, } from 'Plugins/user-management/utils/formValidationUtils' @@ -40,42 +38,6 @@ describe('isEmptyValue', () => { }) }) -describe('shouldDisableApplyButton', () => { - it('should return true when submitting', () => { - expect(shouldDisableApplyButton(true, true, true, { field: 'value' })).toBe(true) - }) - - it('should return true when not dirty and no modified fields', () => { - expect(shouldDisableApplyButton(false, false, true, {})).toBe(true) - }) - - it('should return false when dirty and valid', () => { - expect(shouldDisableApplyButton(false, true, true, {})).toBe(false) - }) - - it('should return false when has modified fields and valid', () => { - expect(shouldDisableApplyButton(false, false, true, { field: 'value' })).toBe(false) - }) - - it('should return true when not valid', () => { - expect(shouldDisableApplyButton(false, true, false, {})).toBe(true) - }) -}) - -describe('hasFormChanges', () => { - it('should return true when dirty', () => { - expect(hasFormChanges(true, {})).toBe(true) - }) - - it('should return true when has modified fields', () => { - expect(hasFormChanges(false, { field: 'value' })).toBe(true) - }) - - it('should return false when not dirty and no modified fields', () => { - expect(hasFormChanges(false, {})).toBe(false) - }) -}) - describe('buildFormOperations', () => { it('should convert modified fields to patch operations', () => { const modifiedFields = { diff --git a/admin-ui/plugins/user-management/common/Constants.ts b/admin-ui/plugins/user-management/common/Constants.ts index 6f5d9f489f..ffce2d347a 100644 --- a/admin-ui/plugins/user-management/common/Constants.ts +++ b/admin-ui/plugins/user-management/common/Constants.ts @@ -2,13 +2,13 @@ export const USER_ROLE_FORM_ATTR = 'role' export const COUNTRY_ATTR = 'c' export const BIRTHDATE_ATTR = 'birthdate' export const USER_PASSWORD_ATTR = 'userPassword' -export const USER_ID_ATTR = 'userId' -export const DISPLAY_NAME_ATTR = 'displayName' -export const MAIL_ATTR = 'mail' +const USER_ID_ATTR = 'userId' +const DISPLAY_NAME_ATTR = 'displayName' +const MAIL_ATTR = 'mail' export const STATUS_ATTR = 'status' export const JANS_STATUS_ATTR = 'jansStatus' -export const UID_ATTR = 'uid' -export const GIVEN_NAME_ATTR = 'givenName' +const UID_ATTR = 'uid' +const GIVEN_NAME_ATTR = 'givenName' export const MIDDLE_NAME_ATTR = 'middleName' export const SN_ATTR = 'sn' export const FAMILY_NAME_ATTR = 'familyName' @@ -16,15 +16,19 @@ export const EMAIL_VERIFIED_ATTR = 'emailVerified' export const CREATED_AT_ATTR = 'createdAt' export const UPDATED_AT_ATTR = 'updatedAt' -export const RESERVED_STANDARD_CLAIMS = [ +export const STANDARD_FORM_FIELDS = [ USER_ID_ATTR, - UID_ATTR, - DISPLAY_NAME_ATTR, MAIL_ATTR, + DISPLAY_NAME_ATTR, STATUS_ATTR, + GIVEN_NAME_ATTR, +] as const + +export const RESERVED_STANDARD_CLAIMS = [ + ...STANDARD_FORM_FIELDS, + UID_ATTR, JANS_STATUS_ATTR, USER_PASSWORD_ATTR, - GIVEN_NAME_ATTR, MIDDLE_NAME_ATTR, SN_ATTR, FAMILY_NAME_ATTR, diff --git a/admin-ui/plugins/user-management/components/UserEditPage.tsx b/admin-ui/plugins/user-management/components/UserEditPage.tsx index 1ad47a1605..07b615234e 100644 --- a/admin-ui/plugins/user-management/components/UserEditPage.tsx +++ b/admin-ui/plugins/user-management/components/UserEditPage.tsx @@ -39,7 +39,7 @@ import { getStandardFieldValues, isPersistenceInfo, } from '../utils' -import { USER_ID_ATTR, MAIL_ATTR, DISPLAY_NAME_ATTR, STATUS_ATTR, GIVEN_NAME_ATTR } from '../common' +import { STANDARD_FORM_FIELDS } from '../common' import { adminUiFeatures } from '@/constants' import { AXIOS_INSTANCE } from 'Orval' import { SESSION_ENDPOINT } from '@/redux/api/backend-api' @@ -150,11 +150,6 @@ const UserEditPage = () => { }) const isSubmitting = updateUserMutation.isPending - const standardFields = useMemo( - () => [USER_ID_ATTR, MAIL_ATTR, DISPLAY_NAME_ATTR, STATUS_ATTR, GIVEN_NAME_ATTR] as const, - [], - ) - const submitData = useCallback( async (values: UserEditFormValues, modifiedFields: ModifiedFields, userMessage: string) => { const baseCustomAttributes = buildCustomAttributesFromValues(values, personAttributes) @@ -163,7 +158,7 @@ const UserEditPage = () => { modifiedFields, personAttributes, ) - const standardFieldValues = getStandardFieldValues(values, standardFields) + const standardFieldValues = getStandardFieldValues(values, STANDARD_FORM_FIELDS) await updateUserMutation.mutateAsync({ data: { @@ -185,7 +180,7 @@ const UserEditPage = () => { } as CustomUser, }) }, - [personAttributes, userDetails, persistenceType, standardFields, updateUserMutation], + [personAttributes, userDetails, persistenceType, updateUserMutation], ) return ( diff --git a/admin-ui/plugins/user-management/components/UserForm.tsx b/admin-ui/plugins/user-management/components/UserForm.tsx index 7c0b55f313..6fdc629e5f 100644 --- a/admin-ui/plugins/user-management/components/UserForm.tsx +++ b/admin-ui/plugins/user-management/components/UserForm.tsx @@ -19,7 +19,8 @@ import UserClaimEntry from './UserClaimEntry' import PasswordChangeModal from './PasswordChangeModal' import AvailableClaimsPanel from './AvailableClaimsPanel' import { buildFormOperations, diffFormValues } from '../utils' -import { getClaimLabel } from '../utils/claimLabelUtils' +import { getClaimLabel, getCountryName } from '../utils/claimLabelUtils' +import { COUNTRY_ATTR } from '../common' import type { FormFieldValue, UserFormProps, @@ -197,7 +198,11 @@ const UserForm = ({ const attributeDef = memoizedPersonAttributes.find((attr) => attr.name === path) const displayName = typeof attributeDef?.displayName === 'string' ? attributeDef.displayName : undefined - return { path, value, label: getClaimLabel(t, path, displayName) } + const displayValue = + path === COUNTRY_ATTR && typeof value === 'string' && value !== '' + ? getCountryName(value) + : value + return { path, value: displayValue, label: getClaimLabel(t, path, displayName) } }), [operations, memoizedPersonAttributes, t], ) diff --git a/admin-ui/plugins/user-management/utils/claimLabelUtils.ts b/admin-ui/plugins/user-management/utils/claimLabelUtils.ts index 65d50a351f..bfbacc7383 100644 --- a/admin-ui/plugins/user-management/utils/claimLabelUtils.ts +++ b/admin-ui/plugins/user-management/utils/claimLabelUtils.ts @@ -1,4 +1,9 @@ import type { TFunction } from 'i18next' +import { countries } from '../common/countries' + +const countryNameByCode = new Map(countries.map((country) => [country.cca2, country.name])) + +export const getCountryName = (code: string): string => countryNameByCode.get(code) ?? code export const getClaimLabelKey = (t: TFunction, name: string, displayName?: string): string => { const key = `claims.${name}` diff --git a/admin-ui/plugins/user-management/utils/formValidationUtils.ts b/admin-ui/plugins/user-management/utils/formValidationUtils.ts index f276c4a36c..d82e985c9b 100644 --- a/admin-ui/plugins/user-management/utils/formValidationUtils.ts +++ b/admin-ui/plugins/user-management/utils/formValidationUtils.ts @@ -46,20 +46,6 @@ export const diffFormValues = ( return result } -export const shouldDisableApplyButton = ( - isSubmitting: boolean, - isDirty: boolean, - isValid: boolean, - modifiedFields: ModifiedFields, -): boolean => { - const hasModifiedFields = Object.keys(modifiedFields).length > 0 - const isFormChanged = isDirty || hasModifiedFields - - if (isSubmitting || !isFormChanged) return true - if (!isValid) return true - return false -} - export const buildFormOperations = (modifiedFields: ModifiedFields) => { return Object.keys(modifiedFields).map((key) => ({ path: key, @@ -67,7 +53,3 @@ export const buildFormOperations = (modifiedFields: ModifiedFields) => { op: 'replace' as const, })) } - -export const hasFormChanges = (isDirty: boolean, modifiedFields: ModifiedFields): boolean => { - return isDirty || Object.keys(modifiedFields).length > 0 -} From b81c8bef2dda741b30fbaa1f6f7365a8d9d26d39 Mon Sep 17 00:00:00 2001 From: faisalsiddique4400 Date: Wed, 17 Jun 2026 16:56:15 +0500 Subject: [PATCH 02/11] knip cleanup further in SAML Signed-off-by: faisalsiddique4400 --- admin-ui/app/utils/regex.ts | 8 +- admin-ui/package.json | 6 - .../WebsiteSsoIdentityBrokeringList.tsx | 331 +++++++----------- .../WebsiteSsoServiceProviderList.tsx | 250 +++++++------ .../components/styles/WebsiteSsoList.style.ts | 45 +++ admin-ui/plugins/saml/helper/index.ts | 5 - admin-ui/plugins/saml/helper/tableUtils.tsx | 44 --- admin-ui/vite.config.ts | 25 -- 8 files changed, 317 insertions(+), 397 deletions(-) create mode 100644 admin-ui/plugins/saml/components/styles/WebsiteSsoList.style.ts delete mode 100644 admin-ui/plugins/saml/helper/tableUtils.tsx diff --git a/admin-ui/app/utils/regex.ts b/admin-ui/app/utils/regex.ts index 45582fa5ed..32e4f2d687 100644 --- a/admin-ui/app/utils/regex.ts +++ b/admin-ui/app/utils/regex.ts @@ -110,8 +110,8 @@ export const regexForBracedKey = (key: string): RegExp => { * Build-tooling regexes * Imported by vite.config.ts, orval mutator, and script/* — * used at build / tooling time only (chunking, bundle reports, - * date-fns ESM resolver, orval enum patcher, orval barrel - * generator, write-method verifier, prettier output parser). + * orval enum patcher, orval barrel generator, write-method + * verifier, prettier output parser). * Not imported by app runtime code. * ============================================================ */ @@ -127,10 +127,6 @@ export const REGEX_NODE_MODULES_PREFIX = /^.*\/node_modules\// export const REGEX_VIRTUAL_MODULE_NULL_PREFIX = /^\0+/ /** Matches built code/style output assets (`.js`, `.mjs`, `.css`, `.wasm`); used to focus the bundle report on optimizable output. */ export const REGEX_CODE_BUILD_ASSET = /\.(?:m?js|css|wasm)$/ -/** Matches the bare `date-fns` import specifier; aliased to date-fns's ESM entry so its CJS build isn't bundled alongside the ESM one (date-fns ships disjoint `.js`/`.mjs` graphs). */ -export const REGEX_DATE_FNS_BARE_SPECIFIER = /^date-fns$/ -/** Matches a `date-fns/` import specifier (excluding `package.json`); capture group [1] is the subpath, used to alias the import to the corresponding ESM `.mjs` file so a single date-fns build ends up in the bundle. */ -export const REGEX_DATE_FNS_SUBPATH_SPECIFIER = /^date-fns\/(?!package\.json)(.+)$/ /** Matches a Prettier --write timing suffix (e.g. "5ms", "123ms"); used to identify file output lines in the format script. */ export const REGEX_PRETTIER_TIMESTAMP = /\d+ms/ /** Extracts the file path from a Prettier --write output line (e.g. "src/foo.ts 5ms (unchanged)" → capture group [1] = "src/foo.ts"). */ diff --git a/admin-ui/package.json b/admin-ui/package.json index 4d922e00a4..6f74bc611c 100644 --- a/admin-ui/package.json +++ b/admin-ui/package.json @@ -86,18 +86,12 @@ "vite-plugin-wasm": "^3.0.0" }, "overrides": { - "@material-table/core": { - "@mui/icons-material": "^7.0.0", - "@mui/material": "$@mui/material", - "@mui/system": "$@mui/material" - }, "react-is": "$react" }, "dependencies": { "@emotion/react": "^11.0.0", "@emotion/styled": "^11.0.0", "@janssenproject/cedarling_wasm": "0.0.338", - "@material-table/core": "6.4.4", "@mui/icons-material": "^9.0.0", "@mui/material": "^9.0.0", "@mui/x-date-pickers": "^9.0.0", diff --git a/admin-ui/plugins/saml/components/WebsiteSsoIdentityBrokeringList.tsx b/admin-ui/plugins/saml/components/WebsiteSsoIdentityBrokeringList.tsx index 88636f2e83..e54e85bd65 100644 --- a/admin-ui/plugins/saml/components/WebsiteSsoIdentityBrokeringList.tsx +++ b/admin-ui/plugins/saml/components/WebsiteSsoIdentityBrokeringList.tsx @@ -1,46 +1,44 @@ -import React, { useCallback, useEffect, useContext, useMemo, useState, useRef } from 'react' -import debounce from 'lodash/debounce' -import MaterialTable, { type Action } from '@material-table/core' +import React, { useCallback, useContext, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import GluuLoader from 'Routes/Apps/Gluu/GluuLoader' import GluuViewWrapper from 'Routes/Apps/Gluu/GluuViewWrapper' import { buildPayload } from 'Utils/auditAction' import { usePermission } from '@/cedarling/hooks/usePermission' -import applicationStyle from '@/routes/Apps/Gluu/styles/applicationStyle' -import { DeleteOutlined } from '@/components/icons' +import { Add, Edit, VisibilityOutlined, DeleteOutlined } from '@/components/icons' import GluuDialog from 'Routes/Apps/Gluu/GluuDialog' -import { TablePagination } from '@mui/material' -import GluuAdvancedSearch from 'Routes/Apps/Gluu/GluuAdvancedSearch' +import { GluuTable } from '@/components/GluuTable' +import { GluuSearchToolbar } from '@/components/GluuSearchToolbar' +import type { ColumnDef, ActionDef, PaginationConfig } from '@/components/GluuTable' import { adminUiFeatures } from '@/constants' -import customColors from '@/customColors' import getThemeColor from 'Context/theme/config' import { ThemeContext } from 'Context/theme/themeContext' import { ADMIN_UI_RESOURCES } from '@/cedarling/utility' import { useAppNavigation, ROUTES } from '@/helpers/navigation' -import { PaperContainer, getIdentityProviderTableCols } from '../helper' +import { getRowsPerPageOptions, usePaginationState } from '@/utils/pagingUtils' import { useIdentityProviders, useDeleteIdentityProvider, type IdentityProvider } from './hooks' -import { DEFAULT_THEME } from '@/context/theme/constants' +import { DEFAULT_THEME, THEME_DARK } from '@/context/theme/constants' import { logger } from '@/utils/logger' +import { useStyles } from './styles/WebsiteSsoList.style' interface DeleteItem { inum?: string displayName?: string } -const DeleteOutlinedIcon = () => +const LIMIT_OPTIONS = getRowsPerPageOptions() +const samlResourceId = ADMIN_UI_RESOURCES.SAML const WebsiteSsoIdentityBrokeringList = React.memo(() => { const theme = useContext(ThemeContext) const selectedTheme = theme?.state?.theme ?? DEFAULT_THEME - const themeColors = getThemeColor(selectedTheme) + const themeColors = useMemo(() => getThemeColor(selectedTheme), [selectedTheme]) + const isDark = selectedTheme === THEME_DARK + const { classes } = useStyles({ isDark, themeColors }) const [modal, setModal] = useState(false) - const [limit, setLimit] = useState(10) - const [pattern, setPattern] = useState(null) - const [searchInput, setSearchInput] = useState('') + const [pattern, setPattern] = useState('') const [item, setItem] = useState({}) - const [pageNumber, setPageNumber] = useState(0) - const prevPatternRef = useRef(null) + const { limit, setLimit, pageNumber, setPageNumber, onPagingSizeSync } = usePaginationState() const { t } = useTranslation() const { navigateToRoute } = useAppNavigation() @@ -70,28 +68,7 @@ const WebsiteSsoIdentityBrokeringList = React.memo(() => { canRead: canReadIdentities, canWrite: canWriteIdentities, canDelete: canDeleteIdentities, - } = usePermission(ADMIN_UI_RESOURCES.SAML) - - const debouncedSetPattern = useMemo( - () => - debounce((value: string) => { - setPattern(value || null) - }, 500), - [], - ) - - useEffect(() => { - return () => { - debouncedSetPattern.cancel() - } - }, [debouncedSetPattern]) - - useEffect(() => { - if (pattern !== prevPatternRef.current) { - prevPatternRef.current = pattern - setPageNumber(0) - } - }, [pattern]) + } = usePermission(samlResourceId) const toggle = useCallback(() => setModal((prev) => !prev), []) @@ -138,218 +115,148 @@ const WebsiteSsoIdentityBrokeringList = React.memo(() => { ) } }, - [buildPayload, deleteIdentityProviderMutation, item.inum, toggle], + [deleteIdentityProviderMutation, item.inum, toggle], ) - const onRowCountChangeClick = useCallback((count: number): void => { - setPageNumber(0) - setLimit(count) - }, []) - - const onPageChangeClick = useCallback((page: number): void => { - setPageNumber(page) - }, []) + const handleSearch = useCallback( + (value: string) => { + setPattern(value) + setPageNumber(0) + }, + [setPageNumber], + ) - const handleRefresh = useCallback((): void => { + const handleRefresh = useCallback(() => { + setPattern('') setPageNumber(0) - }, []) + }, [setPageNumber]) - const handleOptionsChange = useCallback( - (event: React.ChangeEvent): void => { - if (event.target.name === 'limit') { - setLimit(Number(event.target.value)) - } else if (event.target.name === 'pattern') { - const value = event.target.value - setSearchInput(value) - debouncedSetPattern(value) - } - }, - [debouncedSetPattern], - ) + const handlePageChange = useCallback((page: number) => setPageNumber(page), [setPageNumber]) - const handleOptionsKeyDown = useCallback( - (event: React.KeyboardEvent): void => { - if (event.target instanceof HTMLInputElement && event.target.name === 'pattern') { - if (event.key === 'Enter' || event.keyCode === 13) { - const nextPattern = event.target.value - setSearchInput(nextPattern) - debouncedSetPattern.cancel() - setPattern(nextPattern || null) - setPageNumber(0) - } - } + const handleRowsPerPageChange = useCallback( + (rowsPerPage: number) => { + setLimit(rowsPerPage) + setPageNumber(0) }, - [debouncedSetPattern], + [setLimit, setPageNumber], ) - const PaginationWrapper = useCallback( - (): React.ReactElement => ( - | null, page: number): void => { - onPageChangeClick(page) - }} - rowsPerPage={limit} - onRowsPerPageChange={( - event: React.ChangeEvent, - ): void => { - onRowCountChangeClick(Number(event.target.value)) - }} - /> - ), - [pageNumber, totalItems, onPageChangeClick, limit, onRowCountChangeClick], + const columns: ColumnDef[] = useMemo( + () => [ + { key: 'inum', label: t('fields.inum'), sortable: true }, + { key: 'displayName', label: t('fields.displayName'), sortable: true }, + { key: 'enabled', label: t('fields.enabled'), sortable: true }, + ], + [t], ) - const GluuSearch = useCallback((): React.ReactElement => { - return ( - - ) - }, [limit, searchInput, handleOptionsChange, handleOptionsKeyDown]) - - const tableActions = useMemo(() => { - const actions: Action[] = [] + const actions: ActionDef[] = useMemo(() => { + const list: ActionDef[] = [] if (canWriteIdentities) { - actions.push({ - icon: 'edit', - tooltip: `${t('titles.edit_identity_provider')}`, - iconProps: { style: { color: customColors.darkGray } }, - onClick: ( - _event: React.MouseEvent, - rowData: IdentityProvider | IdentityProvider[], - ): void => { - if (Array.isArray(rowData)) return - handleGoToEditPage(rowData) - }, - }) - actions.push({ - icon: 'add', - tooltip: `${t('titles.create_identity_provider')}`, - iconProps: { color: 'primary' }, - isFreeAction: true, - onClick: () => handleGoToAddPage(), + list.push({ + icon: , + tooltip: t('titles.edit_identity_provider'), + onClick: (row) => handleGoToEditPage(row), }) } if (canReadIdentities) { - actions.push({ - icon: 'visibility', - tooltip: `${t('titles.view_identity_provider')}`, - onClick: ( - _event: React.MouseEvent, - rowData: IdentityProvider | IdentityProvider[], - ): void => { - if (Array.isArray(rowData)) return - handleGoToEditPage(rowData, true) - }, + list.push({ + icon: , + tooltip: t('titles.view_identity_provider'), + onClick: (row) => handleGoToEditPage(row, true), }) } if (canDeleteIdentities) { - actions.push({ - icon: DeleteOutlinedIcon, - iconProps: { color: 'secondary' }, - tooltip: `${t('titles.delete_identity_provider')}`, - onClick: ( - _event: React.MouseEvent, - rowData: IdentityProvider | IdentityProvider[], - ): void => { - if (Array.isArray(rowData)) return - handleDelete(rowData) - }, + list.push({ + icon: , + tooltip: t('titles.delete_identity_provider'), + onClick: handleDelete, }) } - const handleSearchIconClickNoop = (): void => {} - actions.push({ - icon: GluuSearch, - tooltip: `${t('messages.advanced_search')}`, - iconProps: { color: 'primary' }, - isFreeAction: true, - onClick: handleSearchIconClickNoop, - }) - actions.push({ - icon: 'refresh', - tooltip: `${t('messages.refresh')}`, - iconProps: { color: 'primary' }, - isFreeAction: true, - onClick: handleRefresh, - } as Action & { 'data-testid'?: string }) - return actions + return list }, [ canWriteIdentities, canReadIdentities, canDeleteIdentities, t, + classes, handleGoToEditPage, - handleGoToAddPage, handleDelete, - handleRefresh, - GluuSearch, ]) - const tableColumns = useMemo(() => getIdentityProviderTableCols(t), [t]) - - const headerStyle = useMemo( - () => - ({ - ...applicationStyle.tableHeaderStyle, - backgroundColor: themeColors.background, - color: themeColors.fontColor, - }) as React.CSSProperties, - [themeColors.background, themeColors.fontColor], - ) - - const tableOptions = useMemo( + const pagination: PaginationConfig = useMemo( () => ({ - search: false, - selection: false, - idSynonym: 'inum', - pageSize: limit, - headerStyle, - actionsColumnIndex: -1, + page: pageNumber, + rowsPerPage: limit, + totalItems, + rowsPerPageOptions: LIMIT_OPTIONS, + onPageChange: handlePageChange, + onRowsPerPageChange: handleRowsPerPageChange, }), - [limit, headerStyle], + [pageNumber, limit, totalItems, handlePageChange, handleRowsPerPageChange], ) - const tableComponents = useMemo( + const primaryAction = useMemo( () => ({ - Container: PaperContainer, - Pagination: PaginationWrapper, + label: t('titles.create_identity_provider'), + icon: , + onClick: handleGoToAddPage, + disabled: !canWriteIdentities, }), - [PaginationWrapper], + [t, classes.addIcon, handleGoToAddPage, canWriteIdentities], + ) + + const getRowKey = useCallback( + (row: IdentityProvider, index: number) => row.inum ?? `row-${index}`, + [], ) return ( - - - - {canDeleteIdentities && ( - - )} +
+ +
+
+ +
+
+ +
+ + columns={columns} + data={items} + loading={false} + pagination={pagination} + onPagingSizeSync={onPagingSizeSync} + actions={actions} + getRowKey={getRowKey} + emptyMessage={t('messages.no_data')} + /> +
+
+ {canDeleteIdentities && ( + + )} +
) }) diff --git a/admin-ui/plugins/saml/components/WebsiteSsoServiceProviderList.tsx b/admin-ui/plugins/saml/components/WebsiteSsoServiceProviderList.tsx index 1ab77cd3ed..5213d794f3 100644 --- a/admin-ui/plugins/saml/components/WebsiteSsoServiceProviderList.tsx +++ b/admin-ui/plugins/saml/components/WebsiteSsoServiceProviderList.tsx @@ -1,42 +1,57 @@ import React, { useCallback, useContext, useMemo, useState } from 'react' -import MaterialTable, { type Action } from '@material-table/core' import { useTranslation } from 'react-i18next' import GluuLoader from 'Routes/Apps/Gluu/GluuLoader' import GluuViewWrapper from 'Routes/Apps/Gluu/GluuViewWrapper' import { buildPayload } from 'Utils/auditAction' import type { UserAction } from 'Utils/types' import { usePermission } from '@/cedarling/hooks/usePermission' -import applicationStyle from '@/routes/Apps/Gluu/styles/applicationStyle' import { ThemeContext } from 'Context/theme/themeContext' import getThemeColor from 'Context/theme/config' -import { DeleteOutlined } from '@/components/icons' +import { Add, Edit, VisibilityOutlined, DeleteOutlined } from '@/components/icons' import GluuDialog from 'Routes/Apps/Gluu/GluuDialog' +import { GluuTable } from '@/components/GluuTable' +import { GluuSearchToolbar } from '@/components/GluuSearchToolbar' +import type { ColumnDef, ActionDef, PaginationConfig } from '@/components/GluuTable' import { adminUiFeatures } from '@/constants' -import { PaperContainer, getServiceProviderTableCols } from '../helper' -import customColors from '@/customColors' import { ADMIN_UI_RESOURCES } from '@/cedarling/utility' import { useAppNavigation, ROUTES } from '@/helpers/navigation' +import { getRowsPerPageOptions, usePaginationState } from '@/utils/pagingUtils' import { useTrustRelationships, useDeleteTrustRelationshipMutation, type TrustRelationship, } from './hooks' -import { DEFAULT_THEME } from '@/context/theme/constants' +import { DEFAULT_THEME, THEME_DARK } from '@/context/theme/constants' import { logger } from '@/utils/logger' +import { useStyles } from './styles/WebsiteSsoList.style' interface DeleteItem { inum: string displayName?: string } -const DeleteOutlinedIcon = () => +const LIMIT_OPTIONS = getRowsPerPageOptions() +const samlResourceId = ADMIN_UI_RESOURCES.SAML + +const matchesPattern = (provider: TrustRelationship, pattern: string): boolean => { + if (!pattern) return true + const needle = pattern.toLowerCase() + return [provider.inum, provider.displayName].some((field) => + (field ?? '').toLowerCase().includes(needle), + ) +} const WebsiteSsoServiceProviderList = React.memo(() => { const theme = useContext(ThemeContext) const selectedTheme = useMemo(() => theme?.state?.theme ?? DEFAULT_THEME, [theme?.state?.theme]) const themeColors = useMemo(() => getThemeColor(selectedTheme), [selectedTheme]) + const isDark = selectedTheme === THEME_DARK + const { classes } = useStyles({ isDark, themeColors }) + const [modal, setModal] = useState(false) const [item, setItem] = useState({ inum: '' }) + const [pattern, setPattern] = useState('') + const { limit, setLimit, pageNumber, setPageNumber, onPagingSizeSync } = usePaginationState() const toggle = useCallback(() => setModal((prev) => !prev), []) @@ -57,7 +72,7 @@ const WebsiteSsoServiceProviderList = React.memo(() => { canRead: canReadWebsiteSsoServiceProviders, canWrite: canWriteWebsiteSsoServiceProviders, canDelete: canDeleteWebsiteSsoServiceProviders, - } = usePermission(ADMIN_UI_RESOURCES.SAML) + } = usePermission(samlResourceId) const handleGoToEditPage = useCallback( (rowData: TrustRelationship, viewOnly?: boolean) => { @@ -102,119 +117,156 @@ const WebsiteSsoServiceProviderList = React.memo(() => { [deleteTrustRelationshipMutation, item.inum, toggle], ) - const handleRefresh = useCallback((): void => { + const handleSearch = useCallback( + (value: string) => { + setPattern(value) + setPageNumber(0) + }, + [setPageNumber], + ) + + const handleRefresh = useCallback(() => { + setPattern('') + setPageNumber(0) refetch() - }, [refetch]) + }, [setPageNumber, refetch]) + + const handlePageChange = useCallback((page: number) => setPageNumber(page), [setPageNumber]) + + const handleRowsPerPageChange = useCallback( + (rowsPerPage: number) => { + setLimit(rowsPerPage) + setPageNumber(0) + }, + [setLimit, setPageNumber], + ) + + const filteredProviders = useMemo( + () => websiteSsoServiceProviders.filter((provider) => matchesPattern(provider, pattern)), + [websiteSsoServiceProviders, pattern], + ) - const tableActions = useMemo(() => { - const actions: Action[] = [] + const pagedProviders = useMemo( + () => filteredProviders.slice(pageNumber * limit, pageNumber * limit + limit), + [filteredProviders, pageNumber, limit], + ) + + const columns: ColumnDef[] = useMemo( + () => [ + { key: 'inum', label: t('fields.inum'), sortable: true }, + { key: 'displayName', label: t('fields.displayName'), sortable: true }, + { key: 'enabled', label: t('fields.enabled'), sortable: true }, + ], + [t], + ) + + const actions: ActionDef[] = useMemo(() => { + const list: ActionDef[] = [] if (canWriteWebsiteSsoServiceProviders) { - actions.push({ - icon: 'edit', - tooltip: `${t('messages.edit_service_provider')}`, - iconProps: { style: { color: customColors.darkGray } }, - onClick: ( - _event: React.MouseEvent, - rowData: TrustRelationship | TrustRelationship[], - ): void => { - if (Array.isArray(rowData)) return - handleGoToEditPage(rowData) - }, - }) - actions.push({ - icon: 'add', - tooltip: `${t('messages.add_service_provider')}`, - iconProps: { color: 'primary' }, - isFreeAction: true, - onClick: () => handleGoToAddPage(), + list.push({ + icon: , + tooltip: t('messages.edit_service_provider'), + onClick: (row) => handleGoToEditPage(row), }) } if (canReadWebsiteSsoServiceProviders) { - actions.push({ - icon: 'visibility', - tooltip: `${t('messages.view_service_provider')}`, - onClick: ( - _event: React.MouseEvent, - rowData: TrustRelationship | TrustRelationship[], - ): void => { - if (Array.isArray(rowData)) return - handleGoToEditPage(rowData, true) - }, + list.push({ + icon: , + tooltip: t('messages.view_service_provider'), + onClick: (row) => handleGoToEditPage(row, true), }) } if (canDeleteWebsiteSsoServiceProviders) { - actions.push({ - icon: DeleteOutlinedIcon, - iconProps: { color: 'secondary' }, - tooltip: `${t('messages.delete_service_provider')}`, - onClick: ( - _event: React.MouseEvent, - rowData: TrustRelationship | TrustRelationship[], - ): void => { - if (Array.isArray(rowData)) return - handleDelete(rowData) - }, + list.push({ + icon: , + tooltip: t('messages.delete_service_provider'), + onClick: handleDelete, }) } - actions.push({ - icon: 'refresh', - tooltip: `${t('messages.refresh')}`, - iconProps: { color: 'primary' }, - isFreeAction: true, - onClick: handleRefresh, - }) - return actions + return list }, [ - canReadWebsiteSsoServiceProviders, canWriteWebsiteSsoServiceProviders, + canReadWebsiteSsoServiceProviders, canDeleteWebsiteSsoServiceProviders, t, + classes, handleGoToEditPage, - handleGoToAddPage, handleDelete, - handleRefresh, ]) - const tableColumns = useMemo(() => getServiceProviderTableCols(t), [t]) + const pagination: PaginationConfig = useMemo( + () => ({ + page: pageNumber, + rowsPerPage: limit, + totalItems: filteredProviders.length, + rowsPerPageOptions: LIMIT_OPTIONS, + onPageChange: handlePageChange, + onRowsPerPageChange: handleRowsPerPageChange, + }), + [pageNumber, limit, filteredProviders.length, handlePageChange, handleRowsPerPageChange], + ) + + const primaryAction = useMemo( + () => ({ + label: t('messages.add_service_provider'), + icon: , + onClick: handleGoToAddPage, + disabled: !canWriteWebsiteSsoServiceProviders, + }), + [t, classes.addIcon, handleGoToAddPage, canWriteWebsiteSsoServiceProviders], + ) + + const getRowKey = useCallback( + (row: TrustRelationship, index: number) => row.inum ?? `row-${index}`, + [], + ) return ( - - - - {canDeleteWebsiteSsoServiceProviders && ( - - )} +
+ +
+
+ +
+
+ +
+ + columns={columns} + data={pagedProviders} + loading={false} + pagination={pagination} + onPagingSizeSync={onPagingSizeSync} + actions={actions} + getRowKey={getRowKey} + emptyMessage={t('messages.no_data')} + /> +
+
+ {canDeleteWebsiteSsoServiceProviders && ( + + )} +
) }) diff --git a/admin-ui/plugins/saml/components/styles/WebsiteSsoList.style.ts b/admin-ui/plugins/saml/components/styles/WebsiteSsoList.style.ts new file mode 100644 index 0000000000..9649b4c5fe --- /dev/null +++ b/admin-ui/plugins/saml/components/styles/WebsiteSsoList.style.ts @@ -0,0 +1,45 @@ +import { makeStyles } from 'tss-react/mui' +import type { ThemeConfig } from '@/context/theme/config' +import { BORDER_RADIUS, ICON_SIZE, SPACING } from '@/constants' +import { getCardBorderStyle } from '@/styles/cardBorderStyles' +import { createSearchCardStyle } from '@/styles/searchCardStyle' +import { fontFamily } from '@/styles/fonts' + +const CARD_INNER_PADDING = 20 + +export const useStyles = makeStyles<{ isDark: boolean; themeColors: ThemeConfig }>()(( + _, + { isDark, themeColors }, +) => { + const cardBorderStyle = getCardBorderStyle({ isDark, borderRadius: BORDER_RADIUS.DEFAULT }) + const cardBg = themeColors.settings?.cardBackground ?? themeColors.card.background + return { + page: { fontFamily, paddingTop: SPACING.PAGE }, + searchCard: createSearchCardStyle({ cardBg, isDark }), + searchCardContent: { + position: 'relative' as const, + zIndex: 2, + isolation: 'isolate' as const, + pointerEvents: 'auto' as const, + }, + tableCard: { + 'width': '100%', + 'maxWidth': '100%', + 'minWidth': 0, + 'marginTop': SPACING.PAGE, + 'backgroundColor': cardBg, + ...cardBorderStyle, + 'borderRadius': BORDER_RADIUS.DEFAULT, + 'padding': `${CARD_INNER_PADDING}px`, + 'position': 'relative' as const, + 'overflow': 'visible', + 'boxSizing': 'border-box' as const, + '& table td': { verticalAlign: 'middle' }, + '& table th': { verticalAlign: 'middle' }, + }, + editIcon: { fontSize: ICON_SIZE.SM }, + deleteIcon: { fontSize: ICON_SIZE.SM }, + viewIcon: { fontSize: ICON_SIZE.SM }, + addIcon: { fontSize: ICON_SIZE.MD }, + } +}) diff --git a/admin-ui/plugins/saml/helper/index.ts b/admin-ui/plugins/saml/helper/index.ts index c29f45a1ea..9e1c94e744 100644 --- a/admin-ui/plugins/saml/helper/index.ts +++ b/admin-ui/plugins/saml/helper/index.ts @@ -13,8 +13,3 @@ export { buildIdentityProviderPayload, buildWebsiteSsoServiceProviderPayload, } from './utils' -export { - PaperContainer, - getIdentityProviderTableCols, - getServiceProviderTableCols, -} from './tableUtils' diff --git a/admin-ui/plugins/saml/helper/tableUtils.tsx b/admin-ui/plugins/saml/helper/tableUtils.tsx deleted file mode 100644 index d739bac3a7..0000000000 --- a/admin-ui/plugins/saml/helper/tableUtils.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import React from 'react' -import { Paper } from '@mui/material' -import type { TFunction } from 'i18next' - -export interface TableColumn { - title: string - field: string -} - -export const getIdentityProviderTableCols = (t: TFunction): TableColumn[] => [ - { - title: `${t('fields.inum')}`, - field: 'inum', - }, - { - title: `${t('fields.displayName')}`, - field: 'displayName', - }, - { - title: `${t('fields.enabled')}`, - field: 'enabled', - }, -] - -export const getServiceProviderTableCols = (t: TFunction): TableColumn[] => [ - { - title: `${t('fields.inum')}`, - field: 'inum', - }, - { - title: `${t('fields.displayName')}`, - field: 'displayName', - }, - { - title: `${t('fields.enabled')}`, - field: 'enabled', - }, -] - -export const PaperContainer = React.memo((props: React.ComponentProps) => ( - -)) - -PaperContainer.displayName = 'PaperContainer' diff --git a/admin-ui/vite.config.ts b/admin-ui/vite.config.ts index b3e3cfee28..4a8a924b04 100644 --- a/admin-ui/vite.config.ts +++ b/admin-ui/vite.config.ts @@ -7,8 +7,6 @@ import { SondaVitePlugin } from 'sonda' import { REGEX_BACKSLASH, REGEX_CODE_BUILD_ASSET, - REGEX_DATE_FNS_BARE_SPECIFIER, - REGEX_DATE_FNS_SUBPATH_SPECIFIER, REGEX_FORWARD_SLASH, REGEX_NODE_MODULES_PREFIX, REGEX_NODE_MODULES_SEGMENT, @@ -31,27 +29,6 @@ const timingPlugin = () => { } } -const DATE_FNS_PACKAGE_DIR = path.resolve(process.cwd(), 'node_modules/date-fns') - -const dateFnsEsmResolverPlugin = () => { - return { - name: 'admin-ui:date-fns-esm', - enforce: 'pre' as const, - resolveId(source: string): string | null { - let mjsPath: string | null = null - if (REGEX_DATE_FNS_BARE_SPECIFIER.test(source)) { - mjsPath = path.join(DATE_FNS_PACKAGE_DIR, 'index.mjs') - } else { - const subpath = REGEX_DATE_FNS_SUBPATH_SPECIFIER.exec(source)?.[1] - if (subpath) { - mjsPath = path.join(DATE_FNS_PACKAGE_DIR, `${subpath}.mjs`) - } - } - return mjsPath && existsSync(mjsPath) ? mjsPath : null - }, - } -} - const REGEX_WASM_ASSET = /\.wasm$/ const wasmPreloadPlugin = (base: string) => { @@ -188,7 +165,6 @@ const FEATURE_GROUPS: ReadonlyArray { base, publicDir: 'public', plugins: [ - dateFnsEsmResolverPlugin(), timingPlugin(), wasm(), wasmPreloadPlugin(base), From b641fce4cced23136f7ebee64c5a583f88e1c44b Mon Sep 17 00:00:00 2001 From: faisalsiddique4400 Date: Thu, 18 Jun 2026 13:08:25 +0500 Subject: [PATCH 03/11] feat(admin-ui): upgrade React to v19 and align codebase with modern React standards (#2880) Signed-off-by: faisalsiddique4400 --- admin-ui/.markdownlint-cli2.cjs | 1 + .../Accordion/AccordionIndicator.tsx | 14 ++++++--- .../components/BootstrapWrappers/index.tsx | 7 ++--- .../app/components/BootstrapWrappers/types.ts | 3 ++ .../components/GluuDropdown/GluuDropdown.tsx | 6 ++-- .../GluuModalShell/GluuModalShell.tsx | 4 +-- .../GluuPageContent/GluuPageContent.tsx | 4 +-- .../app/components/GluuTable/GluuTable.tsx | 2 +- admin-ui/app/components/Layout/Layout.tsx | 15 ++++++---- .../app/components/Layout/LayoutContent.tsx | 4 +-- .../app/components/Layout/LayoutNavbar.tsx | 6 +++- .../app/components/Layout/withPageConfig.tsx | 11 ++++--- .../app/components/LicenseScreens/ApiKey.tsx | 4 +-- .../LicenseScreens/GenerateLicenseCard.tsx | 4 +-- .../app/components/ModalLayer/ModalLayer.tsx | 4 +-- .../app/components/Notifications/index.tsx | 4 +-- .../components/SidebarMenu/SidebarMenu.tsx | 16 +++------- .../SidebarMenu/SidebarMenuItem.tsx | 30 +++++++++++-------- admin-ui/app/components/Wizard/Wizard.tsx | 18 ++++++----- admin-ui/app/context/theme/themeContext.tsx | 6 ++-- admin-ui/app/routes/Apps/Gluu/GluuAlert.tsx | 10 +++---- .../app/routes/Apps/Gluu/GluuAppSidebar.tsx | 4 +-- admin-ui/app/routes/Apps/Gluu/GluuDialog.tsx | 4 +-- .../routes/Apps/Gluu/GluuFormDetailRow.tsx | 4 +-- .../app/routes/Apps/Gluu/GluuInlineInput.tsx | 4 +-- admin-ui/app/routes/Apps/Gluu/GluuLoader.tsx | 4 +-- .../routes/Apps/Gluu/GluuScriptErrorModal.tsx | 4 +-- .../Apps/Gluu/GluuSessionTimeoutDialog.tsx | 4 +-- admin-ui/app/routes/Apps/Gluu/GluuText.tsx | 4 +-- .../app/routes/Apps/Gluu/GluuUploadFile.tsx | 4 +-- .../app/routes/Apps/Gluu/LanguageMenu.tsx | 4 +-- .../routes/Apps/Gluu/hooks/useNavbarTheme.ts | 4 +-- .../Gluu/types/GluuComponentPropsTypes.ts | 6 +++- .../app/routes/Apps/Profile/ProfilePage.tsx | 4 +-- .../app/routes/Dashboards/DashboardPage.tsx | 4 +-- admin-ui/app/routes/Pages/ByeBye.tsx | 4 +-- .../components/LogoThemed/LogoThemed.tsx | 4 +-- admin-ui/app/utils/ApiKeyRedirect.tsx | 4 +-- admin-ui/app/utils/UploadSSA.tsx | 4 +-- admin-ui/eslint.config.cjs | 3 ++ admin-ui/jest.config.ts | 1 + admin-ui/package.json | 8 ++--- .../components/AuthServerPropertiesPage.tsx | 2 +- .../components/JsonPropertyBuilder.tsx | 2 +- .../__tests__/helpers/agamaTestUtils.tsx | 2 +- .../helpers/authenticationTestUtils.tsx | 2 +- .../components/ConfigApiPropertiesForm.tsx | 2 +- .../components/ConfigApiPropertiesPage.tsx | 2 +- .../JsonPropertyBuilderConfigApi.tsx | 2 +- .../ClientActiveTokenDetailPage.tsx | 1 + .../components/ClientActiveTokens.tsx | 2 +- .../OidcClients/components/ClientAddPage.tsx | 2 +- .../OidcClients/components/ClientEditPage.tsx | 2 +- .../components/ClientLogoutPanel.tsx | 2 +- .../Scopes/components/ScopeListPage.tsx | 4 +-- .../Ssa/__tests__/helpers/ssaTestUtils.tsx | 2 +- .../AvailableCustomAttributesPanel.tsx | 2 +- .../Metrics/ActivityBarChart.test.tsx | 7 +++-- .../Metrics/OnboardingTimeChart.test.tsx | 7 +++-- .../WebsiteSsoIdentityBrokeringList.tsx | 4 +-- .../WebsiteSsoServiceProviderList.tsx | 4 +-- .../components/CustomScriptListPage.tsx | 4 +-- .../components/UserClaimsAddPage.tsx | 2 +- .../components/UserClaimsEditPage.tsx | 2 +- .../components/UserClaimsListPage.tsx | 4 +-- .../components/UserClaimsViewPage.tsx | 2 +- .../helpers/userManagementTestUtils.tsx | 2 +- .../components/User2FADevicesModal.tsx | 4 +-- .../user-management/components/UserList.tsx | 2 +- 69 files changed, 181 insertions(+), 153 deletions(-) diff --git a/admin-ui/.markdownlint-cli2.cjs b/admin-ui/.markdownlint-cli2.cjs index 7bff800cd8..5c33274984 100644 --- a/admin-ui/.markdownlint-cli2.cjs +++ b/admin-ui/.markdownlint-cli2.cjs @@ -10,6 +10,7 @@ module.exports = { 'dist', 'coverage', 'jans_config_api_orval', + 'jans_config_api', '.husky/_', '.claude', 'CHANGELOG.md', diff --git a/admin-ui/app/components/Accordion/AccordionIndicator.tsx b/admin-ui/app/components/Accordion/AccordionIndicator.tsx index 11460d713b..5f82ad0293 100755 --- a/admin-ui/app/components/Accordion/AccordionIndicator.tsx +++ b/admin-ui/app/components/Accordion/AccordionIndicator.tsx @@ -18,11 +18,17 @@ export const AccordionIndicator: React.FC = ({ {({ isOpen }) => isOpen - ? React.cloneElement(open as React.ReactElement, { - className: clsx(className, (open as React.ReactElement).props.className), + ? React.cloneElement(open as React.ReactElement<{ className?: string }>, { + className: clsx( + className, + (open as React.ReactElement<{ className?: string }>).props.className, + ), }) - : React.cloneElement(closed as React.ReactElement, { - className: clsx(className, (closed as React.ReactElement).props.className), + : React.cloneElement(closed as React.ReactElement<{ className?: string }>, { + className: clsx( + className, + (closed as React.ReactElement<{ className?: string }>).props.className, + ), }) } diff --git a/admin-ui/app/components/BootstrapWrappers/index.tsx b/admin-ui/app/components/BootstrapWrappers/index.tsx index d6e0a18396..d6c91ff015 100644 --- a/admin-ui/app/components/BootstrapWrappers/index.tsx +++ b/admin-ui/app/components/BootstrapWrappers/index.tsx @@ -134,10 +134,7 @@ export const Alert: React.FC = ({ ) } -export const Input = React.forwardRef< - HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement, - InputProps ->(({ type = 'text', className, children, ...props }, ref) => { +export const Input = ({ ref, type = 'text', className, children, ...props }: InputProps) => { if (type === 'select') { return (