Skip to content

Commit a0d2290

Browse files
authored
feat: enhance facet filtering to fetch json and update facet state (#735) (#745)
* feat: enhance facet filtering to fetch json and update facet state (#735) * chore: update package (#735) * wip * wip * wip * wip * wip --------- Co-authored-by: Fran McDade <18710366+frano-m@users.noreply.github.com>
1 parent 7731f0f commit a0d2290

File tree

18 files changed

+594
-282
lines changed

18 files changed

+594
-282
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,5 @@ __pycache__/
77
.DS_Store
88
.idea
99
.env
10+
11+
databiosphere-findable-ui-47.0.2.tgz

package-lock.json

Lines changed: 253 additions & 271 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export const ADORNMENT_TYPE = {
2+
DEFAULT: "default",
3+
SUBMITTABLE: "submittable",
4+
SUBMITTING: "submitting",
5+
} as const;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import styled from "@emotion/styled";
2+
import { ArrowUpwardRounded } from "@mui/icons-material";
3+
import { PALETTE } from "../../../../../../../../styles/common/constants/palette";
4+
import { LoadingIcon } from "../../../../../../../common/CustomIcon/components/LoadingIcon/loadingIcon";
5+
6+
export const StyledLoadingIcon = styled(LoadingIcon)`
7+
&& {
8+
color: ${PALETTE.PRIMARY_MAIN};
9+
10+
.Mui-focused & {
11+
color: ${PALETTE.PRIMARY_MAIN};
12+
}
13+
}
14+
`;
15+
16+
export const StyledArrowUpwardRounded = styled(ArrowUpwardRounded)`
17+
&& {
18+
color: ${PALETTE.INK_LIGHT};
19+
20+
.Mui-focused & {
21+
color: ${PALETTE.INK_LIGHT};
22+
}
23+
}
24+
`;
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { IconButton } from "@mui/material";
2+
import React from "react";
3+
import { ICON_BUTTON_PROPS } from "../../../../../../../../styles/common/mui/iconButton";
4+
import { SVG_ICON_PROPS } from "../../../../../../../../styles/common/mui/svgIcon";
5+
import { AiIcon } from "../../../../../../../common/CustomIcon/components/AiIcon/aiIcon";
6+
import { ADORNMENT_TYPE } from "./constants";
7+
import {
8+
StyledArrowUpwardRounded,
9+
StyledLoadingIcon,
10+
} from "./endAdornment.styles";
11+
import { EndAdornmentProps } from "./types";
12+
13+
export const EndAdornment = ({
14+
adornmentType,
15+
}: EndAdornmentProps): JSX.Element => {
16+
switch (adornmentType) {
17+
case ADORNMENT_TYPE.SUBMITTING:
18+
return (
19+
<StyledLoadingIcon
20+
color={SVG_ICON_PROPS.COLOR.PRIMARY}
21+
fontSize={SVG_ICON_PROPS.FONT_SIZE.SMALL}
22+
/>
23+
);
24+
case ADORNMENT_TYPE.SUBMITTABLE:
25+
return (
26+
<IconButton
27+
color={ICON_BUTTON_PROPS.COLOR.SECONDARY}
28+
edge={ICON_BUTTON_PROPS.EDGE.END}
29+
size={ICON_BUTTON_PROPS.SIZE.XSMALL}
30+
type="submit"
31+
>
32+
<StyledArrowUpwardRounded fontSize={SVG_ICON_PROPS.FONT_SIZE.SMALL} />
33+
</IconButton>
34+
);
35+
case ADORNMENT_TYPE.DEFAULT:
36+
default:
37+
return <AiIcon />;
38+
}
39+
};
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { ADORNMENT_TYPE } from "./constants";
2+
3+
export interface EndAdornmentProps {
4+
adornmentType: (typeof ADORNMENT_TYPE)[keyof typeof ADORNMENT_TYPE];
5+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { ADORNMENT_TYPE } from "./constants";
2+
3+
/**
4+
* Returns the end adornment type of the input i.e. "default", "submittable", "submitting".
5+
* @param isDirty - Form is dirty.
6+
* @param isSubmiting - Form is submitting.
7+
* @returns End adornment type.
8+
*/
9+
export function getEndAdornmentType(
10+
isDirty: boolean,
11+
isSubmiting: boolean
12+
): (typeof ADORNMENT_TYPE)[keyof typeof ADORNMENT_TYPE] {
13+
if (isSubmiting) return ADORNMENT_TYPE.SUBMITTING;
14+
if (isDirty) return ADORNMENT_TYPE.SUBMITTABLE;
15+
return ADORNMENT_TYPE.DEFAULT;
16+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { OutlinedInputProps } from "@mui/material";
2+
import { TEXT_FIELD_PROPS as MUI_TEXT_FIELD_PROPS } from "../../../../../../styles/common/mui/textField";
3+
4+
export const OUTLINED_INPUT_PROPS: OutlinedInputProps = {
5+
autoComplete: "off",
6+
color: MUI_TEXT_FIELD_PROPS.COLOR.SECONDARY,
7+
fullWidth: true,
8+
id: "query-to-facets",
9+
name: "query-to-facets",
10+
placeholder: "Search all filters...",
11+
size: MUI_TEXT_FIELD_PROPS.SIZE.SMALL,
12+
};
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import styled from "@emotion/styled";
2+
import { OutlinedInput } from "@mui/material";
3+
import { PALETTE } from "../../../../../../styles/common/constants/palette";
4+
5+
export const StyledForm = styled.form`
6+
grid-column: 1 / -1;
7+
`;
8+
9+
export const StyledOutlinedInput = styled(OutlinedInput)`
10+
&:not(:placeholder-shown) {
11+
.MuiOutlinedInput-input {
12+
color: ${PALETTE.INK_MAIN};
13+
}
14+
}
15+
`;
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import React, { FormEvent, useCallback, useState } from "react";
2+
import { useExploreState } from "../../../../../../hooks/useExploreState";
3+
import { ExploreActionKind } from "../../../../../../providers/exploreState";
4+
import { getFormValue } from "../../../../../../utils/form";
5+
import { EndAdornment } from "./components/EndAdornment/endAdornment";
6+
import { getEndAdornmentType } from "./components/EndAdornment/utils";
7+
import { OUTLINED_INPUT_PROPS } from "./constants";
8+
import { StyledForm, StyledOutlinedInput } from "./facetAssistant.styles";
9+
import { mapResponse } from "./utils";
10+
11+
/**
12+
* AI-powered facet assistant component.
13+
* Converts a user query into facet filters (PoC implementation).
14+
*/
15+
16+
export const FacetAssistant = (): JSX.Element => {
17+
const { exploreDispatch } = useExploreState();
18+
const [isDirty, setIsDirty] = useState<boolean>(false);
19+
const [isError, setIsError] = useState<boolean>(false);
20+
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
21+
22+
const onSubmit = useCallback(
23+
async (e: FormEvent<HTMLFormElement>): Promise<void> => {
24+
e.preventDefault();
25+
26+
// Get the form value.
27+
const formValue = getFormValue(e, "query-to-facets");
28+
if (!formValue) return;
29+
30+
setIsSubmitting(true);
31+
setIsError(false);
32+
33+
try {
34+
const res = await fetch(
35+
"http://localhost:8000/api/v0/facets?mode=llm",
36+
{
37+
body: JSON.stringify({ query: formValue }),
38+
headers: {
39+
"Content-Type": "application/json",
40+
},
41+
method: "POST",
42+
}
43+
);
44+
45+
if (!res.ok) {
46+
setIsError(true);
47+
return;
48+
}
49+
50+
const data = await res.json();
51+
52+
// Map the response data to facet filters.
53+
const filters = mapResponse(data);
54+
55+
// Dispatch the update entity filters action.
56+
exploreDispatch({
57+
payload: { filters },
58+
type: ExploreActionKind.UpdateEntityFilters,
59+
});
60+
61+
// Reset the form.
62+
setIsDirty(false);
63+
(e.target as HTMLFormElement).reset();
64+
} catch (err) {
65+
console.error(err);
66+
setIsError(true);
67+
} finally {
68+
setIsSubmitting(false);
69+
}
70+
},
71+
[exploreDispatch]
72+
);
73+
74+
return (
75+
<StyledForm onSubmit={onSubmit}>
76+
<StyledOutlinedInput
77+
{...OUTLINED_INPUT_PROPS}
78+
endAdornment={
79+
<EndAdornment
80+
adornmentType={getEndAdornmentType(isDirty, isSubmitting)}
81+
/>
82+
}
83+
error={isError}
84+
onChange={(): void => setIsDirty(true)}
85+
/>
86+
</StyledForm>
87+
);
88+
};

0 commit comments

Comments
 (0)