forked from freeipa/freeipa-webui
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fixes: freeipa#567 Signed-off-by: Mark Reynolds <[email protected]>
- Loading branch information
1 parent
09e940d
commit 25d9cf4
Showing
20 changed files
with
1,574 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
import React from "react"; | ||
// PatternFly | ||
import { | ||
Divider, | ||
MenuToggle, | ||
MenuToggleElement, | ||
MenuSearch, | ||
MenuSearchInput, | ||
Dropdown, | ||
DropdownItem, | ||
DropdownList, | ||
SearchInput, | ||
} from "@patternfly/react-core"; | ||
// Utils | ||
import { | ||
IPAParamDefinition, | ||
getParamProperties, | ||
} from "src/utils/ipaObjectUtils"; | ||
import { updateIpaObject } from "src/utils/ipaObjectUtils"; | ||
|
||
interface IPAParamDefinitionDropdown extends IPAParamDefinition { | ||
id?: string; | ||
setIpaObject?: (ipaObject: Record<string, unknown>) => void; | ||
options: string[]; | ||
ariaLabelledBy?: string; | ||
onSearch: (value: string) => void; | ||
} | ||
|
||
const IpaDropdownSearch = (props: IPAParamDefinitionDropdown) => { | ||
const [isOpen, setIsOpen] = React.useState(false); | ||
const [searchValue, setSearchValue] = React.useState(""); | ||
|
||
const { value } = getParamProperties(props); | ||
const ipaObject = props.ipaObject || {}; | ||
|
||
const onSelect = ( | ||
_event: React.MouseEvent<Element, MouseEvent> | undefined, | ||
selection: string | number | undefined | ||
) => { | ||
if (ipaObject && props.setIpaObject !== undefined) { | ||
updateIpaObject(ipaObject, props.setIpaObject, selection, props.name); | ||
} | ||
props.onSearch(""); | ||
setSearchValue(""); | ||
setIsOpen(false); | ||
}; | ||
|
||
const onToggleClick = () => { | ||
setIsOpen(!isOpen); | ||
}; | ||
|
||
// Removed selected value from options | ||
const options = props.options.filter((item) => item !== value); | ||
|
||
return ( | ||
<Dropdown | ||
id={props.id || "dropdown-search"} | ||
isOpen={isOpen} | ||
onSelect={onSelect} | ||
onOpenChange={(isOpen: boolean) => setIsOpen(isOpen)} | ||
toggle={(toggleRef: React.Ref<MenuToggleElement>) => ( | ||
<MenuToggle | ||
ref={toggleRef} | ||
isFullWidth | ||
onClick={onToggleClick} | ||
isExpanded={isOpen} | ||
> | ||
{value} | ||
</MenuToggle> | ||
)} | ||
ouiaId="BasicDropdown" | ||
shouldFocusToggleOnSelect | ||
isScrollable | ||
> | ||
<MenuSearch> | ||
<MenuSearchInput> | ||
<SearchInput | ||
value={searchValue} | ||
placeholder="Search" | ||
onChange={(_event, value) => setSearchValue(value)} | ||
onSearch={() => props.onSearch(searchValue)} | ||
onClear={() => { | ||
setSearchValue(""); | ||
props.onSearch(""); | ||
}} | ||
aria-labelledby="pf-v5-context-selector-search-button-id-1" | ||
/> | ||
</MenuSearchInput> | ||
</MenuSearch> | ||
<Divider /> | ||
<DropdownList> | ||
{options.map((option, index) => ( | ||
<DropdownItem value={option} key={index}> | ||
{option} | ||
</DropdownItem> | ||
))} | ||
</DropdownList> | ||
</Dropdown> | ||
); | ||
}; | ||
|
||
export default IpaDropdownSearch; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
import { useState, useEffect } from "react"; | ||
|
||
// RPC | ||
import { useGetObjectMetadataQuery } from "src/services/rpc"; | ||
import { useGetConfigQuery } from "src/services/rpcConfig"; | ||
// Data types | ||
import { Config, Metadata } from "src/utils/datatypes/globalDataTypes"; | ||
|
||
type ConfigSettingsData = { | ||
isLoading: boolean; | ||
isFetching: boolean; | ||
modified: boolean; | ||
setModified: (value: boolean) => void; | ||
resetValues: () => void; | ||
metadata: Metadata; | ||
originalConfig: Partial<Config>; | ||
config: Partial<Config>; | ||
setConfig: (hostGroup: Partial<Config>) => void; | ||
refetch: () => void; | ||
modifiedValues: () => Partial<Config>; | ||
}; | ||
|
||
const useConfigSettings = (): ConfigSettingsData => { | ||
// [API call] Metadata | ||
const metadataQuery = useGetObjectMetadataQuery(); | ||
const metadata = metadataQuery.data || {}; | ||
const metadataLoading = metadataQuery.isLoading; | ||
|
||
// [API call] Config | ||
const configQuery = useGetConfigQuery(); | ||
const configData = configQuery.data; | ||
const isFullDataLoading = configQuery.isLoading; | ||
const [modified, setModified] = useState(false); | ||
const [config, setConfig] = useState<Partial<Config>>({}); | ||
|
||
useEffect(() => { | ||
if (configData && !configQuery.isFetching) { | ||
setConfig({ ...configData }); | ||
} | ||
}, [configData, configQuery.isFetching]); | ||
|
||
const settings = { | ||
isLoading: metadataLoading || isFullDataLoading, | ||
isFetching: configQuery.isFetching, | ||
modified, | ||
setModified, | ||
metadata, | ||
// eslint-disable-next-line @typescript-eslint/no-empty-function | ||
resetValues: () => {}, | ||
originalConfig: config, | ||
config, | ||
setConfig, | ||
refetch: configQuery.refetch, | ||
modifiedValues: () => config, | ||
} as ConfigSettingsData; | ||
|
||
if (configData) { | ||
settings.originalConfig = configData || {}; | ||
} else { | ||
settings.originalConfig = {}; | ||
} | ||
|
||
const getModifiedValues = (): Partial<Config> => { | ||
if (!configData) { | ||
return {}; | ||
} | ||
|
||
const modifiedValues = {}; | ||
for (const [key, value] of Object.entries(config)) { | ||
if (Array.isArray(value)) { | ||
// Using 'JSON.stringify' when comparing arrays (to prevent data type false positives) | ||
if (JSON.stringify(configData[key]) !== JSON.stringify(value)) { | ||
modifiedValues[key] = value; | ||
} | ||
} else if (configData[key] !== value) { | ||
modifiedValues[key] = value; | ||
} | ||
} | ||
return modifiedValues; | ||
}; | ||
settings.modifiedValues = getModifiedValues; | ||
|
||
// Detect any change in 'originalConfig' and 'config' objects | ||
useEffect(() => { | ||
if (!configData) { | ||
return; | ||
} | ||
let modified = false; | ||
for (const [key, value] of Object.entries(config)) { | ||
if (Array.isArray(value)) { | ||
// Using 'JSON.stringify' when comparing arrays (to prevent data type false positives) | ||
if (JSON.stringify(configData[key]) !== JSON.stringify(value)) { | ||
modified = true; | ||
break; | ||
} | ||
} else { | ||
if (configData[key].toString() !== value.toString()) { | ||
modified = true; | ||
break; | ||
} | ||
} | ||
} | ||
setModified(modified); | ||
}, [config, configData]); | ||
|
||
const onResetValues = () => { | ||
setModified(false); | ||
}; | ||
settings.resetValues = onResetValues; | ||
|
||
return settings; | ||
}; | ||
|
||
export { useConfigSettings }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import React from "react"; | ||
// PatternFly | ||
import { Flex, FlexItem, Form, FormGroup } from "@patternfly/react-core"; | ||
// Data types | ||
import { Metadata } from "src/utils/datatypes/globalDataTypes"; | ||
// Form | ||
import IpaTextArea from "src/components/Form/IpaTextArea"; | ||
import ConfigObjectclassTable from "./ConfigObjectclassTable"; | ||
|
||
interface PropsToGroupOptions { | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
ipaObject: Record<string, any>; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
recordOnChange: (ipaObject: Record<string, any>) => void; | ||
metadata: Metadata; | ||
} | ||
|
||
const ConfigGroupOptions = (props: PropsToGroupOptions) => { | ||
return ( | ||
<Flex direction={{ default: "column" }} flex={{ default: "flex_1" }}> | ||
<FlexItem flex={{ default: "flex_1" }}> | ||
<Form className="pf-v5-u-mb-lg pf-v5-u-mt-lg" isHorizontal> | ||
<FormGroup | ||
label="Group search fields" | ||
fieldId="ipagroupsearchfields" | ||
isRequired | ||
> | ||
<IpaTextArea | ||
name="ipagroupsearchfields" | ||
ipaObject={props.ipaObject} | ||
onChange={props.recordOnChange} | ||
objectName="config" | ||
metadata={props.metadata} | ||
/> | ||
</FormGroup> | ||
</Form> | ||
<Form className="pf-v5-u-mb-lg pf-v5-u-mt-lg"> | ||
<FormGroup | ||
label="Default group objectclasses" | ||
fieldId="ipagroupobjectclasses" | ||
> | ||
<ConfigObjectclassTable | ||
title="Default group objectclasses" | ||
name="ipagroupobjectclasses" | ||
ipaObject={props.ipaObject} | ||
onChange={props.recordOnChange} | ||
objectName="config" | ||
metadata={props.metadata} | ||
/> | ||
</FormGroup> | ||
</Form> | ||
</FlexItem> | ||
</Flex> | ||
); | ||
}; | ||
|
||
export default ConfigGroupOptions; |
Oops, something went wrong.