Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI support for Filtering Organizations by Metadata Attributes #6667

Merged
merged 5 commits into from
Aug 6, 2024
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
8 changes: 8 additions & 0 deletions .changeset/poor-schools-retire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@wso2is/admin.organizations.v1": minor
"@wso2is/react-components": minor
"@wso2is/admin.core.v1": minor
"@wso2is/i18n": patch
---

UI support for Filtering Organizations by Metadata Attributes
Original file line number Diff line number Diff line change
Expand Up @@ -27,30 +27,21 @@ import {
PrimaryButton,
SessionTimedOutContext
} from "@wso2is/react-components";
import React, { CSSProperties, FunctionComponent, ReactElement, useState } from "react";
import React, { CSSProperties, FunctionComponent, ReactElement, ReactNode, useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { Divider, Form, Grid } from "semantic-ui-react";
import { getAdvancedSearchIcons } from "../configs";

/**
* Filter attribute field identifier.
*/
const FILTER_ATTRIBUTE_FIELD_IDENTIFIER: string = "filterAttribute";

/**
* Filter condition field identifier.
*/
const FILTER_CONDITION_FIELD_IDENTIFIER: string = "filterCondition";

/**
* Filter value field identifier.
*/
const FILTER_VALUES_FIELD_IDENTIFIER: string = "filterValues";
import { AdvanceSearchConstants } from "../constants/advance-search";

/**
* Prop types for the application search component.
*/
export interface AdvancedSearchWithBasicFiltersPropsInterface extends TestableComponentInterface {

/**
* Children node form field passed from parent
*/
children?: ReactNode;
/**
* Default Search attribute. ex: "name"
*/
Expand All @@ -71,10 +62,26 @@ export interface AdvancedSearchWithBasicFiltersPropsInterface extends TestableCo
* Fill color.
*/
fill?: AdvancedSearchPropsInterface[ "fill" ];
/**
* Callback to be triggered on advance search close.
*/
onClose?: () => void;
/**
* Callback to be triggered on filter query change.
*/
onFilter: (query: string) => void;
/**
* Callback to be triggered on filter attribute option change.
*/
onFilterAttributeOptionsChange?: (values) => void;
/**
* Callback to be triggered on submit error.
*/
onSubmitError?: () => boolean;
/**
* Callback to be get custom query.
*/
getQuery?: (values) => string;
/**
* Filter attributes options.
*/
Expand Down Expand Up @@ -152,6 +159,7 @@ export const AdvancedSearchWithBasicFilters: FunctionComponent<AdvancedSearchWit
): ReactElement => {

const {
children,
defaultSearchAttribute,
defaultSearchOperator,
disableSearchFilterDropdown,
Expand All @@ -163,7 +171,11 @@ export const AdvancedSearchWithBasicFilters: FunctionComponent<AdvancedSearchWit
filterConditionsPlaceholder,
filterAttributePlaceholder,
filterValuePlaceholder,
onClose,
onFilter,
onFilterAttributeOptionsChange,
onSubmitError,
getQuery,
placeholder,
predefinedDefaultSearchStrategy,
resetButtonLabel,
Expand All @@ -181,19 +193,34 @@ export const AdvancedSearchWithBasicFilters: FunctionComponent<AdvancedSearchWit
const [ isFiltersReset, setIsFiltersReset ] = useState<boolean>(false);
const [ externalSearchQuery, setExternalSearchQuery ] = useState<string>("");
const sessionTimedOut: boolean = React.useContext(SessionTimedOutContext);
const formRef: React.RefObject<HTMLDivElement> = useRef<HTMLDivElement>(null);

/**
* Handles the form submit.
*
* @param values - Form values.
*/
const handleFormSubmit = (values: Map<string, string | string[]>): void => {
const query: string = values.get(FILTER_ATTRIBUTE_FIELD_IDENTIFIER)
+ " "
+ values.get(FILTER_CONDITION_FIELD_IDENTIFIER)
+ " "
+ values?.get(FILTER_VALUES_FIELD_IDENTIFIER);

if (onSubmitError) {
const shouldReturn: boolean = onSubmitError();

if (shouldReturn) {
return;
}
}
let query: string;
const customQuery: string = getQuery ? getQuery(values) : null;

if (customQuery !== null) {
query = customQuery;
} else {
query = values.get(AdvanceSearchConstants.FILTER_ATTRIBUTE_FIELD_IDENTIFIER)
+ " "
+ values.get(AdvanceSearchConstants.FILTER_CONDITION_FIELD_IDENTIFIER)
+ " "
+ values?.get(AdvanceSearchConstants.FILTER_VALUES_FIELD_IDENTIFIER);
}
setExternalSearchQuery(query);
onFilter(query);
setIsFormSubmitted(true);
Expand Down Expand Up @@ -247,6 +274,28 @@ export const AdvancedSearchWithBasicFilters: FunctionComponent<AdvancedSearchWit
}
};

/**
* Handles any clicks outside the form.
*/
const handleClickOutside = (event: MouseEvent) => {
if (formRef.current && !formRef.current.contains(event.target as Node)) {
if (onClose) {
onClose();
}
}
};

/**
* Adds an event listener for detecting clicks outside the form on component mount.
*/
useEffect(() => {
document.addEventListener("mousedown", handleClickOutside);

return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, []);

/**
* Default filter condition options.
*/
Expand Down Expand Up @@ -305,6 +354,8 @@ export const AdvancedSearchWithBasicFilters: FunctionComponent<AdvancedSearchWit
onSubmit={ (values: Map<string, FormValue>) => handleFormSubmit(values) }
resetState={ isFiltersReset }
onChange={ () => setIsFiltersReset(false) }
onSubmitError={ () => {onSubmitError?.();} }
ref={ formRef }
>
<Field
children={
Expand All @@ -319,7 +370,7 @@ export const AdvancedSearchWithBasicFilters: FunctionComponent<AdvancedSearchWit
// TODO: Enable this once default value is working properly for the dropdowns.
// readOnly={ filterAttributeOptions.length === 1 }
label={ t("console:common.advancedSearch.form.inputs.filterAttribute.label") }
name={ FILTER_ATTRIBUTE_FIELD_IDENTIFIER }
name={ AdvanceSearchConstants.FILTER_ATTRIBUTE_FIELD_IDENTIFIER }
placeholder={
filterAttributePlaceholder
? filterAttributePlaceholder
Expand All @@ -335,7 +386,9 @@ export const AdvancedSearchWithBasicFilters: FunctionComponent<AdvancedSearchWit
value={ defaultSearchAttribute }
data-testid={ `${ testId }-filter-attribute-dropdown` }
data-componentid={ `${ testId }-filter-attribute-dropdown` }
listen={ (values: Map<string, FormValue>) => onFilterAttributeOptionsChange?.(values) }
/>
{ children }
<Form.Group widths="equal">
<Field
children={
Expand All @@ -359,7 +412,7 @@ export const AdvancedSearchWithBasicFilters: FunctionComponent<AdvancedSearchWit
label={
t("console:common.advancedSearch.form.inputs.filterCondition.label")
}
name={ FILTER_CONDITION_FIELD_IDENTIFIER }
name={ AdvanceSearchConstants.FILTER_CONDITION_FIELD_IDENTIFIER }
placeholder={
filterConditionsPlaceholder
? filterConditionsPlaceholder
Expand All @@ -375,7 +428,7 @@ export const AdvancedSearchWithBasicFilters: FunctionComponent<AdvancedSearchWit
/>
<Field
label={ t("console:common.advancedSearch.form.inputs.filterValue.label") }
name={ FILTER_VALUES_FIELD_IDENTIFIER }
name={ AdvanceSearchConstants.FILTER_VALUES_FIELD_IDENTIFIER }
placeholder={
filterValuePlaceholder
? filterValuePlaceholder
Expand Down
34 changes: 34 additions & 0 deletions features/admin.core.v1/constants/advance-search.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com).
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

export class AdvanceSearchConstants {
/**
* Filter attribute field identifier.
*/
public static readonly FILTER_ATTRIBUTE_FIELD_IDENTIFIER: string = "filterAttribute";

/**
* Filter condition field identifier.
*/
public static readonly FILTER_CONDITION_FIELD_IDENTIFIER: string = "filterCondition";

/**
* Filter value field identifier.
*/
public static readonly FILTER_VALUES_FIELD_IDENTIFIER: string = "filterValues";
}
7 changes: 5 additions & 2 deletions features/admin.organizations.v1/api/organization.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com).
* Copyright (c) 2023-2024, WSO2 LLC. (https://www.wso2.com).
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
Expand All @@ -24,7 +24,10 @@ import {
HttpResponse
} from "@asgardeo/auth-react";
import { store } from "@wso2is/admin.core.v1";
import useRequest, { RequestErrorInterface, RequestResultInterface } from "@wso2is/admin.core.v1/hooks/use-request";
import useRequest, {
RequestErrorInterface,
RequestResultInterface
} from "@wso2is/admin.core.v1/hooks/use-request";
import { IdentityAppsApiException } from "@wso2is/core/exceptions";
import { HttpMethods } from "@wso2is/core/models";
import { AxiosError, AxiosRequestConfig, AxiosResponse } from "axios";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
* Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com).
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import { store } from "@wso2is/admin.core.v1";
import useRequest, {
RequestConfigInterface,
RequestErrorInterface,
RequestResultInterface
} from "@wso2is/admin.core.v1/hooks/use-request";
import { HttpMethods } from "@wso2is/core/models";
import { OrganizationsMetaAttributesListInterface } from "../models";

/**
* Hook to get a list of organizations' meta attributes.
*
* @param filter - The filter query.
* @param limit - The maximum number of meta attributes to return.
* @param after - The previous range of data to be returned.
* @param before - The next range of data to be returned.
* @param recursive - Whether we need to do a recursive search.
* @param isRoot - Whether the organization is the root
*
* @returns Organizations Meta Attributes GET hook.
*/
export const useGetOrganizationsMetaAttributes =
<Data = OrganizationsMetaAttributesListInterface, Error = RequestErrorInterface>(
filter?: string,
limit?: number,
after?: string,
before?: string,
recursive: boolean = false,
isRoot: boolean = false
): RequestResultInterface<Data, Error> => {
const requestConfig: RequestConfigInterface = {
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
method: HttpMethods.GET,
params: {
after,
before,
filter,
limit,
recursive
},
url: (isRoot
? store.getState().config.endpoints.rootOrganization
: store.getState().config.endpoints.organizations) + "/organizations/meta-attributes"
};

const { data, error, isLoading, isValidating, mutate } = useRequest<Data, Error>(
requestConfig,
{ revalidateIfStale: false }
);

return {
data,
error,
isLoading,
isValidating,
mutate
};
};
32 changes: 16 additions & 16 deletions features/admin.organizations.v1/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
/**
* Copyright (c) 2022, WSO2 Inc. (http://www.wso2.com) All Rights Reserved.
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
* Copyright (c) 2022, WSO2 LLC. (https://www.wso2.com).
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

export * from "./organization-list";
export * from "./add-organization-modal";
Expand Down
Loading
Loading