From dad5339a89ace1c558bb9ab3b338acbd87ea6e36 Mon Sep 17 00:00:00 2001 From: LuizFNJ Date: Thu, 5 Feb 2026 02:42:41 +0100 Subject: [PATCH 1/3] fix: allow users to remove topics from verification requests --- .../ClaimReview/ClaimReviewHeader.tsx | 4 ++-- .../VerificationRequestDetailDrawer.tsx | 6 +++--- src/components/topics/TopicDisplay.tsx | 5 +++-- src/types/Topic.ts | 18 ++++++++++++++++++ 4 files changed, 26 insertions(+), 7 deletions(-) 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/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/TopicDisplay.tsx b/src/components/topics/TopicDisplay.tsx index cada1c89f..7f2fee680 100644 --- a/src/components/topics/TopicDisplay.tsx +++ b/src/components/topics/TopicDisplay.tsx @@ -7,13 +7,14 @@ import TagDisplay from "./TagDisplay"; import SentenceApi from "../../api/sentenceApi"; import verificationRequestApi from "../../api/verificationRequestApi"; import { ReviewTaskTypeEnum } from "../../machines/reviewTask/enums"; +import { TopicDisplayProps } from "../../types/Topic"; const TopicDisplay = ({ data_hash, topics, reviewTaskType, contentModel = null, -}) => { +}: TopicDisplayProps) => { const [showTopicsForm, setShowTopicsForm] = useState(false); const [topicsArray, setTopicsArray] = useState(topics); const [inputValue, setInputValue] = useState([]); @@ -45,7 +46,7 @@ const TopicDisplay = ({ const handleClose = async (removedTopicValue: any) => { const newTopicsArray = topicsArray.filter( - (topic) => (topic?.value || topic) !== removedTopicValue + (topic) => (topic?.value || topic?.wikidataId || topic) !== removedTopicValue ); const newInputValue = inputValue.filter( (topic) => (topic?.value || topic) !== removedTopicValue diff --git a/src/types/Topic.ts b/src/types/Topic.ts index 4d9cbbf28..3c5b1d043 100644 --- a/src/types/Topic.ts +++ b/src/types/Topic.ts @@ -1,3 +1,6 @@ +import { ObjectId } from "mongodb"; +import { ReviewTaskTypeEnum } from "../machines/reviewTask/enums"; +import { ContentModelEnum } from "./enums"; export interface Topic { _id: string; name: string; @@ -5,3 +8,18 @@ export interface Topic { slug: string; language: string; } + +export interface ManualTopic { + id: ObjectId | string; + label: string; + value: string; +} + +export interface TopicDisplayProps { + data_hash: string; + topics: UnifiedTopic[]; + reviewTaskType: ReviewTaskTypeEnum; + contentModel?: ContentModelEnum | null; +} + +export type UnifiedTopic = Topic | ManualTopic; \ No newline at end of file From 7970913cd32eda665be14edaccbbf932ca77b730 Mon Sep 17 00:00:00 2001 From: LuizFNJ Date: Thu, 5 Feb 2026 20:56:25 +0100 Subject: [PATCH 2/3] Improving nomenclature --- src/components/topics/TopicDisplay.tsx | 32 +++++++++---------- src/components/topics/TopicForm.tsx | 6 ++-- src/components/topics/TopicOrImpactSelect.tsx | 8 ++--- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/components/topics/TopicDisplay.tsx b/src/components/topics/TopicDisplay.tsx index 7f2fee680..8a6f85abf 100644 --- a/src/components/topics/TopicDisplay.tsx +++ b/src/components/topics/TopicDisplay.tsx @@ -17,42 +17,42 @@ const TopicDisplay = ({ }: TopicDisplayProps) => { 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]); const handleClose = async (removedTopicValue: any) => { const newTopicsArray = topicsArray.filter( (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( @@ -85,7 +85,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..120e98aa0 100644 --- a/src/components/topics/TopicForm.tsx +++ b/src/components/topics/TopicForm.tsx @@ -14,7 +14,7 @@ interface ITopicForm { data_hash: string; topicsArray: any[]; setTopicsArray: (topicsArray) => void; - setInputValue: (inputValue) => void; + setSelectedTags: (inputValue) => void; tags: any[]; reviewTaskType: string; } @@ -24,7 +24,7 @@ const TopicForm = ({ data_hash, topicsArray, setTopicsArray, - setInputValue, + setSelectedTags, tags, reviewTaskType, }: ITopicForm) => { @@ -92,7 +92,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..50e205717 100644 --- a/src/components/topics/TopicOrImpactSelect.tsx +++ b/src/components/topics/TopicOrImpactSelect.tsx @@ -14,7 +14,7 @@ const MultiSelectAutocomplete = ({ onChange, isLoading, placeholder, - setInputValue, + setSelectedTags, setIsLoading, }) => { const { t } = useTranslation(); @@ -77,9 +77,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 || "" From 3b4f744d804bff0a18cb6e2d71012a9ce40f72a2 Mon Sep 17 00:00:00 2001 From: LuizFNJ Date: Thu, 5 Feb 2026 21:55:03 +0100 Subject: [PATCH 3/3] refactor/docs: improve types and add explanatory comments --- .../ImpactAreaSelect.tsx | 21 ++++------ src/components/topics/TagDisplay.tsx | 9 +---- src/components/topics/TopicDisplay.tsx | 9 ++++- src/components/topics/TopicForm.tsx | 12 +----- src/components/topics/TopicOrImpactSelect.tsx | 3 +- src/types/Topic.ts | 40 +++++++++++++++++-- 6 files changed, 56 insertions(+), 38 deletions(-) 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/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 8a6f85abf..e2a902b3a 100644 --- a/src/components/topics/TopicDisplay.tsx +++ b/src/components/topics/TopicDisplay.tsx @@ -7,14 +7,14 @@ import TagDisplay from "./TagDisplay"; import SentenceApi from "../../api/sentenceApi"; import verificationRequestApi from "../../api/verificationRequestApi"; import { ReviewTaskTypeEnum } from "../../machines/reviewTask/enums"; -import { TopicDisplayProps } from "../../types/Topic"; +import { ITopicDisplay } from "../../types/Topic"; const TopicDisplay = ({ data_hash, topics, reviewTaskType, contentModel = null, -}: TopicDisplayProps) => { +}: ITopicDisplay) => { const [showTopicsForm, setShowTopicsForm] = useState(false); const [topicsArray, setTopicsArray] = useState(topics); const [selectedTags, setSelectedTags] = useState([]); @@ -44,7 +44,12 @@ const TopicDisplay = ({ 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?.wikidataId || topic) !== removedTopicValue ); diff --git a/src/components/topics/TopicForm.tsx b/src/components/topics/TopicForm.tsx index 120e98aa0..acce8e387 100644 --- a/src/components/topics/TopicForm.tsx +++ b/src/components/topics/TopicForm.tsx @@ -5,19 +5,9 @@ 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; - setSelectedTags: (inputValue) => void; - tags: any[]; - reviewTaskType: string; -} +import { ITopicForm } from "../../types/Topic"; const TopicForm = ({ contentModel, diff --git a/src/components/topics/TopicOrImpactSelect.tsx b/src/components/topics/TopicOrImpactSelect.tsx index 50e205717..54a97898e 100644 --- a/src/components/topics/TopicOrImpactSelect.tsx +++ b/src/components/topics/TopicOrImpactSelect.tsx @@ -8,6 +8,7 @@ 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, @@ -16,7 +17,7 @@ const MultiSelectAutocomplete = ({ placeholder, setSelectedTags, setIsLoading, -}) => { +}: IMultiSelectAutocomplete) => { const { t } = useTranslation(); const [options, setOptions] = useState([]); const dispatch = useDispatch(); diff --git a/src/types/Topic.ts b/src/types/Topic.ts index 3c5b1d043..650ef18ac 100644 --- a/src/types/Topic.ts +++ b/src/types/Topic.ts @@ -1,4 +1,4 @@ -import { ObjectId } from "mongodb"; +import React from "react"; import { ReviewTaskTypeEnum } from "../machines/reviewTask/enums"; import { ContentModelEnum } from "./enums"; export interface Topic { @@ -10,16 +10,48 @@ export interface Topic { } export interface ManualTopic { - id: ObjectId | string; + id?: string; + aliases?: string[]; + displayLabel?: string; label: string; + matchedAlias?: string | null; value: string; } -export interface TopicDisplayProps { +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 type UnifiedTopic = Topic | ManualTopic; \ No newline at end of file +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>; +}