diff --git a/website/src/components/FlaggableElement.tsx b/website/src/components/FlaggableElement.tsx index eaf32ab0..79431fc5 100644 --- a/website/src/components/FlaggableElement.tsx +++ b/website/src/components/FlaggableElement.tsx @@ -22,7 +22,7 @@ import { useId, } from "@chakra-ui/react"; import { FlagIcon, QuestionMarkCircleIcon } from "@heroicons/react/20/solid"; -import { useEffect, useState } from "react"; +import { useEffect, useReducer } from "react"; import fetcher from "src/lib/fetcher"; import poster from "src/lib/poster"; import { Message } from "src/types/Conversation"; @@ -30,10 +30,38 @@ import { colors } from "styles/Theme/colors"; import useSWR from "swr"; import useSWRMutation from "swr/mutation"; -interface textFlagLabels { - attributeName: string; - labelText: string; - additionalExplanation?: string; +interface Label { + name: string; + display_text: string; + help_text: string; +} + +interface LoadLabelsAction { + type: "load_labels"; + labels: Label[]; +} + +interface UpdateValueAction { + type: "update_value"; + label_index: number; + value: number; +} + +interface ToggleLabelAction { + type: "toggle_label"; + label_index: number; + check: boolean; +} + +interface LabelValue { + label: Label; + checked: boolean; + value: number; +} + +interface FlagReportState { + label_values: LabelValue[]; + submittable: boolean; } interface FlaggableElementProps { @@ -42,9 +70,34 @@ interface FlaggableElementProps { } export const FlaggableElement = (props: FlaggableElementProps) => { - const [labels, setLabels] = useState([]); - const [checkboxValues, setCheckboxValues] = useState([]); - const [sliderValues, setSliderValues] = useState([]); + const [report, updateReport] = useReducer( + (state: FlagReportState, action: LoadLabelsAction | UpdateValueAction | ToggleLabelAction): FlagReportState => { + const makeState = (label_values: LabelValue[]): FlagReportState => { + const submittable = label_values.map(({ checked }) => checked).some(Boolean); + return { label_values, submittable }; + }; + + switch (action.type) { + case "load_labels": + return makeState( + action.labels.map((label) => { + return { label, checked: false, value: 1 }; + }) + ); + case "toggle_label": { + const values_copy = state.label_values.slice(); + values_copy[action.label_index].checked = action.check; + return makeState(values_copy); + } + case "update_value": { + const values_copy = state.label_values.slice(); + values_copy[action.label_index].value = action.value; + return makeState(values_copy); + } + } + }, + { label_values: [], submittable: false } + ); const [isEditing, setIsEditing] = useBoolean(); const backgroundColor = useColorModeValue("gray.200", "gray.700"); @@ -54,14 +107,7 @@ export const FlaggableElement = (props: FlaggableElementProps) => { return; } const { valid_labels } = data; - const newLabels = valid_labels.map((valid_label) => ({ - attributeName: valid_label.name, - labelText: valid_label.display_text, - additionalExplanation: valid_label.help_text, - })); - setSliderValues(new Array(newLabels.length).fill(1)); - setCheckboxValues(new Array(newLabels.length).fill(false)); - setLabels(newLabels); + updateReport({ type: "load_labels", labels: valid_labels }); }, [data, isLoading]); const { trigger } = useSWRMutation("/api/set_label", poster, { @@ -72,9 +118,9 @@ export const FlaggableElement = (props: FlaggableElementProps) => { const submitResponse = () => { const label_map: Map = new Map(); - labels.forEach((flag, i) => { - if (checkboxValues[i]) { - label_map.set(flag.attributeName, sliderValues[i]); + report.label_values.forEach(({ label, checked, value }) => { + if (checked) { + label_map.set(label.name, value); } }); trigger({ @@ -84,19 +130,11 @@ export const FlaggableElement = (props: FlaggableElementProps) => { }); }; - const handleCheckboxState = (isChecked, idx) => { - setCheckboxValues( - checkboxValues.map((val, i) => { - return i === idx ? isChecked : val; - }) - ); + const handleCheckboxState = (checked, label_index) => { + updateReport({ type: "toggle_label", label_index, check: checked }); }; - const handleSliderState = (newVal, idx) => { - setSliderValues( - sliderValues.map((val, i) => { - return i === idx ? newVal : val; - }) - ); + const handleSliderState = (value, label_index) => { + updateReport({ type: "update_value", label_index, value }); }; return ( @@ -127,20 +165,20 @@ export const FlaggableElement = (props: FlaggableElementProps) => { - {labels.map((option, i) => ( + {report.label_values.map(({ label, checked, value }, i) => ( ))}