diff --git a/src/components/ClaimReview/ClaimReviewHeader.tsx b/src/components/ClaimReview/ClaimReviewHeader.tsx index a7816d64a..ed16d99e7 100644 --- a/src/components/ClaimReview/ClaimReviewHeader.tsx +++ b/src/components/ClaimReview/ClaimReviewHeader.tsx @@ -74,10 +74,10 @@ const ClaimReviewHeader = ({ /> {showTopicInput && ( )} diff --git a/src/components/VerificationRequest/CreateVerificationRequest/ImpactAreaSelect.tsx b/src/components/VerificationRequest/CreateVerificationRequest/ImpactAreaSelect.tsx index f1dca1ed5..dac8d0a51 100644 --- a/src/components/VerificationRequest/CreateVerificationRequest/ImpactAreaSelect.tsx +++ b/src/components/VerificationRequest/CreateVerificationRequest/ImpactAreaSelect.tsx @@ -1,30 +1,25 @@ -import * as React from "react"; -import { useEffect, useState} from "react"; +import React, { useState, useEffect } from "react"; import MultiSelectAutocomplete from "../../topics/TopicOrImpactSelect"; +import { IImpactAreaSelect, ManualTopic } from "../../../types/Topic"; - -interface ImpactAreaSelectProps { - onChange: (value: string) => void; - placeholder?: string; -} - -const ImpactAreaSelect: React.FC = ({ +const ImpactAreaSelect = ({ onChange, placeholder, -}) => { - const [value, setValue] = useState(""); +}: IImpactAreaSelect) => { + const [value, setValue] = useState([]); const [isLoading, setIsLoading] = useState(false); useEffect(() => { - onChange(value || undefined); + onChange(value); }, []); + return ( ); diff --git a/src/components/VerificationRequest/VerificationRequestDetailDrawer.tsx b/src/components/VerificationRequest/VerificationRequestDetailDrawer.tsx index d76e17ac2..13dacabd9 100644 --- a/src/components/VerificationRequest/VerificationRequestDetailDrawer.tsx +++ b/src/components/VerificationRequest/VerificationRequestDetailDrawer.tsx @@ -460,12 +460,12 @@ const VerificationRequestDetailDrawer: React.FC diff --git a/src/components/topics/TagDisplay.tsx b/src/components/topics/TagDisplay.tsx index 05b57f646..c9c268345 100644 --- a/src/components/topics/TagDisplay.tsx +++ b/src/components/topics/TagDisplay.tsx @@ -6,12 +6,7 @@ import TagsList from "./TagsList"; import { useAtom } from "jotai"; import { isUserLoggedIn } from "../../atoms/currentUser"; import TagDisplayStyled from "./TagDisplay.style"; - -interface ITagDisplay { - handleClose: (removedTopicValue: any) => Promise; - tags: any[]; - setShowTopicsForm: (topicsForm: any) => void; -} +import { ITagDisplay } from "../../types/Topic"; const TagDisplay = ({ handleClose, tags, setShowTopicsForm }: ITagDisplay) => { const [isLoggedIn] = useAtom(isUserLoggedIn); @@ -31,7 +26,7 @@ const TagDisplay = ({ handleClose, tags, setShowTopicsForm }: ITagDisplay) => { color: colors.primary, }} > - {tags.length ? : } + {tags.length ? : } )} diff --git a/src/components/topics/TopicDisplay.tsx b/src/components/topics/TopicDisplay.tsx index cada1c89f..e2a902b3a 100644 --- a/src/components/topics/TopicDisplay.tsx +++ b/src/components/topics/TopicDisplay.tsx @@ -7,51 +7,57 @@ import TagDisplay from "./TagDisplay"; import SentenceApi from "../../api/sentenceApi"; import verificationRequestApi from "../../api/verificationRequestApi"; import { ReviewTaskTypeEnum } from "../../machines/reviewTask/enums"; +import { ITopicDisplay } from "../../types/Topic"; const TopicDisplay = ({ data_hash, topics, reviewTaskType, contentModel = null, -}) => { +}: ITopicDisplay) => { const [showTopicsForm, setShowTopicsForm] = useState(false); const [topicsArray, setTopicsArray] = useState(topics); - const [inputValue, setInputValue] = useState([]); + const [selectedTags, setSelectedTags] = useState([]); const [tags, setTags] = useState([]); const { t } = useTranslation(); useEffect(() => { - const inputValueFormatted = inputValue.map((inputValue) => - inputValue?.value + const formattedSelectedTags = selectedTags.map((selectedTag) => + selectedTag?.value ? { - label: inputValue?.label, - value: inputValue?.value, - aliases: inputValue?.aliases || [], - matchedAlias: inputValue?.matchedAlias || null, + label: selectedTag?.label, + value: selectedTag?.value, + aliases: selectedTag?.aliases || [], + matchedAlias: selectedTag?.matchedAlias || null, } - : inputValue + : selectedTag ); - const filterValues = inputValueFormatted.filter( - (inputValue) => + const filterSelectedTags = formattedSelectedTags.filter( + (selectedTag) => !topicsArray.some( (topic) => (topic?.value || topic) === - (inputValue?.value || inputValue) + (selectedTag?.value || selectedTag) ) ); - setTags(topicsArray?.concat(filterValues) || []); - }, [inputValue, topicsArray]); + setTags(topicsArray?.concat(filterSelectedTags) || []); + }, [selectedTags, topicsArray]); + + console.log(topicsArray,selectedTags) const handleClose = async (removedTopicValue: any) => { + // NOTE: Filtering logic differs due to inconsistent data structures across collections: + // 1. topicsArray: May contain persisted topics from VerificationRequest, requiring 'wikidataId' as an identifier. + // 2. selectedTags: Follows the 'ManualTopic' UI schema, which consistently uses 'value'. const newTopicsArray = topicsArray.filter( - (topic) => (topic?.value || topic) !== removedTopicValue + (topic) => (topic?.value || topic?.wikidataId || topic) !== removedTopicValue ); - const newInputValue = inputValue.filter( + const newSelectedTag = selectedTags.filter( (topic) => (topic?.value || topic) !== removedTopicValue ); setTopicsArray(newTopicsArray); - setInputValue(newInputValue); + setSelectedTags(newSelectedTag); if (reviewTaskType === ReviewTaskTypeEnum.VerificationRequest) { return await verificationRequestApi.deleteVerificationRequestTopic( @@ -84,7 +90,7 @@ const TopicDisplay = ({ data_hash={data_hash} topicsArray={topicsArray} setTopicsArray={setTopicsArray} - setInputValue={setInputValue} + setSelectedTags={setSelectedTags} tags={tags} reviewTaskType={reviewTaskType} /> diff --git a/src/components/topics/TopicForm.tsx b/src/components/topics/TopicForm.tsx index 8c0f538a2..acce8e387 100644 --- a/src/components/topics/TopicForm.tsx +++ b/src/components/topics/TopicForm.tsx @@ -5,26 +5,16 @@ import AletheiaButton from "../Button"; import TopicInputErrorMessages from "./TopicInputErrorMessages"; import { useTranslation } from "next-i18next"; import TopicsApi from "../../api/topicsApi"; -import { ContentModelEnum } from "../../types/enums"; import MultiSelectAutocomplete from "./TopicOrImpactSelect"; import verificationRequestApi from "../../api/verificationRequestApi"; - -interface ITopicForm { - contentModel: ContentModelEnum; - data_hash: string; - topicsArray: any[]; - setTopicsArray: (topicsArray) => void; - setInputValue: (inputValue) => void; - tags: any[]; - reviewTaskType: string; -} +import { ITopicForm } from "../../types/Topic"; const TopicForm = ({ contentModel, data_hash, topicsArray, setTopicsArray, - setInputValue, + setSelectedTags, tags, reviewTaskType, }: ITopicForm) => { @@ -92,7 +82,7 @@ const TopicForm = ({ onChange={onChange} setIsLoading={setIsLoading} isLoading={isLoading} - setInputValue={setInputValue} + setSelectedTags={setSelectedTags} /> )} /> diff --git a/src/components/topics/TopicOrImpactSelect.tsx b/src/components/topics/TopicOrImpactSelect.tsx index 470de7839..54a97898e 100644 --- a/src/components/topics/TopicOrImpactSelect.tsx +++ b/src/components/topics/TopicOrImpactSelect.tsx @@ -8,15 +8,16 @@ import { import { useDispatch } from "react-redux"; import TopicsApi from "../../api/topicsApi"; import { useTranslation } from "react-i18next"; +import { IMultiSelectAutocomplete } from "../../types/Topic"; const MultiSelectAutocomplete = ({ isMultiple = true, onChange, isLoading, placeholder, - setInputValue, + setSelectedTags, setIsLoading, -}) => { +}: IMultiSelectAutocomplete) => { const { t } = useTranslation(); const [options, setOptions] = useState([]); const dispatch = useDispatch(); @@ -77,9 +78,9 @@ const MultiSelectAutocomplete = ({ size="small" options={options} onInputChange={(_, value) => fetchOptions(value)} - onChange={(_, selectedValues) => { - onChange(selectedValues); - setInputValue(selectedValues); + onChange={(_, inputTag) => { + onChange(inputTag); + setSelectedTags(inputTag); }} getOptionLabel={(option) => option.displayLabel || option.label || "" diff --git a/src/types/Topic.ts b/src/types/Topic.ts index 4d9cbbf28..650ef18ac 100644 --- a/src/types/Topic.ts +++ b/src/types/Topic.ts @@ -1,3 +1,6 @@ +import React from "react"; +import { ReviewTaskTypeEnum } from "../machines/reviewTask/enums"; +import { ContentModelEnum } from "./enums"; export interface Topic { _id: string; name: string; @@ -5,3 +8,50 @@ export interface Topic { slug: string; language: string; } + +export interface ManualTopic { + id?: string; + aliases?: string[]; + displayLabel?: string; + label: string; + matchedAlias?: string | null; + value: string; +} + +export type UnifiedTopic = Topic | ManualTopic; +export interface ITagDisplay { + handleClose: (removedTopicValue: string) => Promise; + tags: ManualTopic[]; + setShowTopicsForm: React.Dispatch>; +} + +export interface ITopicForm { + contentModel: ContentModelEnum; + data_hash: string; + topicsArray: UnifiedTopic[]; + setTopicsArray: React.Dispatch>; + setSelectedTags: React.Dispatch>; + tags: ManualTopic[]; + reviewTaskType: ReviewTaskTypeEnum; +} + +export interface ITopicDisplay { + data_hash: string; + topics: UnifiedTopic[]; + reviewTaskType: ReviewTaskTypeEnum; + contentModel?: ContentModelEnum | null; +} + +export interface IImpactAreaSelect { + onChange: (value: ManualTopic[]) => void; + placeholder?: string; +} + +export interface IMultiSelectAutocomplete { + isMultiple?: boolean; + onChange: (value: ManualTopic[]) => void; + isLoading: boolean; + placeholder: string; + setSelectedTags: React.Dispatch>; + setIsLoading: React.Dispatch>; +}