From 4c37202f42728e6871a0487d1cee33e1433bcc5d Mon Sep 17 00:00:00 2001 From: Aditya Khatri Date: Tue, 3 Dec 2024 17:38:46 +0545 Subject: [PATCH] Remove analytical framework mapping and update assisted tagging to work with LLM --- .../AssistItem/AssistPopup/index.tsx | 8 +- .../LeftPaneEntries/AssistItem/index.tsx | 340 +++---------- .../LeftPaneEntries/AssistItem/utils.ts | 323 ++++-------- .../AutoEntriesModal/index.tsx | 329 ++----------- .../SimplifiedTextView/index.tsx | 77 +-- app/components/LeftPaneEntries/index.tsx | 17 +- app/gqlFragments.ts | 8 - app/types/newAnalyticalFramework.ts | 181 +------ .../AnalysisEditModal/index.tsx | 1 - .../AssistedTagging/CellGroup/index.tsx | 36 -- .../AssistedTagging/CellGroup/styles.css | 12 - .../AssistedTagging/CheckButton/index.tsx | 50 -- .../AssistedTagging/CheckButton/styles.css | 23 - .../FrameworkTagRow/AddButton/index.tsx | 103 ---- .../AssistedTagging/FrameworkTagRow/index.tsx | 145 ------ .../FrameworkTagRow/styles.css | 38 -- .../AssistedTagging/TagWithBadge/index.tsx | 57 --- .../AssistedTagging/TagWithBadge/styles.css | 53 -- .../WidgetTagList/Matrix1dTagInput/index.tsx | 183 ------- .../WidgetTagList/Matrix1dTagInput/styles.css | 4 - .../WidgetTagList/Matrix2dTagInput/index.tsx | 465 ------------------ .../WidgetTagList/Matrix2dTagInput/styles.css | 16 - .../OptionTypeTagInput/index.tsx | 135 ----- .../OrganigramTagInput/index.tsx | 132 ----- .../WidgetTagList/OrganigramTagInput/utils.ts | 20 - .../AssistedTagging/WidgetTagList/index.tsx | 150 ------ .../AssistedTagging/WidgetTagList/styles.css | 9 - .../AssistedTagging/index.tsx | 445 ----------------- .../AssistedTagging/styles.css | 106 ---- .../AssistedTagging/utils.test.ts | 419 ---------------- .../AssistedTagging/utils.ts | 128 ----- app/views/AnalyticalFramework/index.tsx | 251 +--------- app/views/AnalyticalFramework/queries.ts | 15 - app/views/AnalyticalFramework/schema.ts | 28 +- app/views/AnalyticalFramework/types.tsx | 11 +- 35 files changed, 262 insertions(+), 4056 deletions(-) delete mode 100644 app/views/AnalyticalFramework/AssistedTagging/CellGroup/index.tsx delete mode 100644 app/views/AnalyticalFramework/AssistedTagging/CellGroup/styles.css delete mode 100644 app/views/AnalyticalFramework/AssistedTagging/CheckButton/index.tsx delete mode 100644 app/views/AnalyticalFramework/AssistedTagging/CheckButton/styles.css delete mode 100644 app/views/AnalyticalFramework/AssistedTagging/FrameworkTagRow/AddButton/index.tsx delete mode 100644 app/views/AnalyticalFramework/AssistedTagging/FrameworkTagRow/index.tsx delete mode 100644 app/views/AnalyticalFramework/AssistedTagging/FrameworkTagRow/styles.css delete mode 100644 app/views/AnalyticalFramework/AssistedTagging/TagWithBadge/index.tsx delete mode 100644 app/views/AnalyticalFramework/AssistedTagging/TagWithBadge/styles.css delete mode 100644 app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/Matrix1dTagInput/index.tsx delete mode 100644 app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/Matrix1dTagInput/styles.css delete mode 100644 app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/Matrix2dTagInput/index.tsx delete mode 100644 app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/Matrix2dTagInput/styles.css delete mode 100644 app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/OptionTypeTagInput/index.tsx delete mode 100644 app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/OrganigramTagInput/index.tsx delete mode 100644 app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/OrganigramTagInput/utils.ts delete mode 100644 app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/index.tsx delete mode 100644 app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/styles.css delete mode 100644 app/views/AnalyticalFramework/AssistedTagging/index.tsx delete mode 100644 app/views/AnalyticalFramework/AssistedTagging/styles.css delete mode 100644 app/views/AnalyticalFramework/AssistedTagging/utils.test.ts delete mode 100644 app/views/AnalyticalFramework/AssistedTagging/utils.ts diff --git a/app/components/LeftPaneEntries/AssistItem/AssistPopup/index.tsx b/app/components/LeftPaneEntries/AssistItem/AssistPopup/index.tsx index b4581b14b8..57c685c8cd 100644 --- a/app/components/LeftPaneEntries/AssistItem/AssistPopup/index.tsx +++ b/app/components/LeftPaneEntries/AssistItem/AssistPopup/index.tsx @@ -12,11 +12,10 @@ import { import EntryInput from '#components/entry/EntryInput'; import { GeoArea } from '#components/GeoMultiSelectInput'; -import { WidgetHint } from '#types/newAnalyticalFramework'; import { Framework } from '#components/entry/types'; import { - PartialEntryType, PartialAttributeType, + PartialEntryType, } from '#components/entry/schema'; import styles from './styles.css'; @@ -35,8 +34,7 @@ interface Props { geoAreaOptions: GeoArea[] | undefined | null; onGeoAreaOptionsChange: React.Dispatch>; predictionsLoading?: boolean; - hints: WidgetHint[] | undefined; - recommendations: PartialAttributeType[] | undefined; + recommendations?: PartialAttributeType[] | undefined; predictionsErrored: boolean; name: NAME; messageText: string | undefined; @@ -62,7 +60,6 @@ function AssistPopup(props: Props(props: Props { const widgetsFromPrimary = frameworkDetails?.primaryTagging?.flatMap( (item) => (item.widgets ?? []), @@ -188,20 +161,12 @@ function AssistItem(props: Props) { ]; return { allWidgets: widgets, - filteredWidgets: widgets.filter((w) => mappingsSupportedWidgets.includes(w.widgetId)), }; }, [ frameworkDetails, ]); - const mappings = frameworkDetails?.predictionTagsMapping; const alert = useAlert(); - const [ - allRecommendations, - setAllRecommendations, - ] = useState(undefined); - - const [allHints, setAllHints] = useState(undefined); const assistPopupRef = useRef< { setShowPopup: React.Dispatch> } @@ -260,165 +225,47 @@ function AssistItem(props: Props) { error, } = useForm(schema, emptyEntry); - const [messageText, setMessageText] = useState(); + const [messageText] = useState(); - const handleMappingsFetch = useCallback(( - predictions: { tags: string[]; locations: GeoArea[]; }, - ) => { - if (predictions.tags.length <= 0 && predictions.locations.length <= 0) { - setMessageText('DEEP could not provide any recommendations for the selected text.'); - return; - } - - setGeoAreaOptions(predictions.locations); - - const matchedMappings = mappings - ?.filter(isCategoricalMappings) - .filter((m) => m.tag && predictions.tags.includes(m.tag)); - - const supportedGeoWidgets = mappings - ?.filter((mappingItem) => mappingItem.widgetType === 'GEO') - ?.map((mappingItem) => mappingItem.widget); - - const { - tempAttrs: recommendedAttributes, - tempHints: widgetsHints, - } = filteredWidgets.reduce( - ( - acc: { tempAttrs: PartialAttributeType[]; tempHints: WidgetHint[]; }, - widget, - ) => { - const { - tempAttrs: oldTempAttrs, - tempHints: oldTempHints, - } = acc; - - if (widget.widgetId === 'MATRIX1D') { - const supportedTags = matchedMappings - ?.filter((m) => m.widget === widget.id) - .filter(filterMatrix1dMappings); - - const attr = createMatrix1dAttr(supportedTags, widget); - return { - tempAttrs: attr ? [...oldTempAttrs, attr] : oldTempAttrs, - tempHints: oldTempHints, - }; - } - if (widget.widgetId === 'MATRIX2D') { - const supportedTags = matchedMappings - ?.filter((m) => m.widget === widget.id) - .filter(filterMatrix2dMappings); - - const attr = createMatrix2dAttr(supportedTags, widget); - - return { - tempAttrs: attr ? [...oldTempAttrs, attr] : oldTempAttrs, - tempHints: oldTempHints, - }; - } - if (widget.widgetId === 'SCALE') { - const supportedTags = matchedMappings - ?.filter((m) => m.widget === widget.id) - .filter(filterScaleMappings); - - const { - attr, - hints, - } = createScaleAttr(supportedTags, widget); - - const hintsWithInfo: WidgetHint | undefined = hints ? { - widgetPk: widget.id, - widgetType: 'SCALE', - hints, - } : undefined; - - return { - tempAttrs: attr ? [...oldTempAttrs, attr] : oldTempAttrs, - tempHints: hintsWithInfo ? [...oldTempHints, hintsWithInfo] : oldTempHints, - }; - } - if (widget.widgetId === 'SELECT') { - const supportedTags = matchedMappings - ?.filter((m) => m.widget === widget.id) - .filter(filterSelectMappings); - - const { - attr, - hints, - } = createSelectAttr(supportedTags, widget); - - const hintsWithInfo: WidgetHint | undefined = hints ? { - widgetPk: widget.id, - widgetType: 'SELECT', - hints, - } : undefined; - - return { - tempAttrs: attr ? [...oldTempAttrs, attr] : oldTempAttrs, - tempHints: hintsWithInfo ? [...oldTempHints, hintsWithInfo] : oldTempHints, - }; - } - if (widget.widgetId === 'MULTISELECT') { - const supportedTags = matchedMappings - ?.filter((m) => m.widget === widget.id) - .filter(filterMultiSelectMappings); - - const attr = createMultiSelectAttr( - supportedTags, - widget, - ); - - return { - tempAttrs: attr ? [...oldTempAttrs, attr] : oldTempAttrs, - tempHints: oldTempHints, - }; - } - if (widget.widgetId === 'ORGANIGRAM') { - const supportedTags = matchedMappings - ?.filter((m) => m.widget === widget.id) - .filter(filterOrganigramMappings); - - const attr = createOrganigramAttr( - supportedTags, - widget, - ); - - return { - tempAttrs: attr ? [...oldTempAttrs, attr] : oldTempAttrs, - tempHints: oldTempHints, - }; - } - if ( - widget.widgetId === 'GEO' - && predictions.locations.length > 0 - && supportedGeoWidgets?.includes(widget.id) - ) { - const attr = createGeoAttr( - predictions.locations, - widget, - ); - - return { - tempAttrs: attr ? [...oldTempAttrs, attr] : oldTempAttrs, - tempHints: oldTempHints, - }; - } - return acc; - }, - { - tempAttrs: [], - tempHints: [], - }, - ); + const [draftEntryId, setDraftEntryId] = useState(undefined); + // FIXME: randomId is used to create different query variables after each poll + // so that apollo doesn't create unnecessary cache + const [randomId, setRandomId] = useState(randomString()); + const [predictionsLoading, setPredictionsLoading] = useState(false); - if (recommendedAttributes.length <= 0 && widgetsHints.length <= 0) { - setMessageText('The provided recommendations for this text did not fit any tags in this project.'); - return; - } + const queryVariables = useMemo(() => ( + draftEntryId && projectId ? ({ + projectId, + draftEntryId, + randomId, + }) : undefined + ), [ + randomId, + projectId, + draftEntryId, + ]); - setAllHints(widgetsHints); - setAllRecommendations(recommendedAttributes); + const [isErrored, setIsErrored] = useState(false); + const [recommendations, setRecommendations] = useState(); + + const handleTagsFetch = useCallback((recommendedTags: ModelTagsType) => { + const newAttributes = allWidgets?.map((widget) => { + if (widget.widgetId === 'MATRIX1D') { + return createMatrix1dAttrFromTags( + recommendedTags[widget.key] as Matrix1dValue, + widget, + ); + } + if (widget.widgetId === 'MATRIX2D') { + return createMatrix2dAttrFromTags( + recommendedTags[widget.key] as Matrix2dValue, + widget, + ); + } + return undefined; + }).filter(isDefined); + setRecommendations(newAttributes); setValue( (oldEntry) => { if (!oldEntry) { @@ -428,67 +275,24 @@ function AssistItem(props: Props) { lead: leadId, excerpt: text, droppedExcerpt: text, - attributes: recommendedAttributes.map((attr) => { - if (attr.widgetType !== 'GEO') { - return attr; - } - // NOTE: Selecting only the 1st recommendation - return ({ - ...attr, - data: { - value: attr?.data?.value.slice(0, 1) ?? [], - }, - }); - }), + attributes: newAttributes, }; } return { ...oldEntry, - attributes: recommendedAttributes.map((attr) => { - if (attr.widgetType !== 'GEO') { - return attr; - } - // NOTE: Selecting only the 1st recommendation - return ({ - ...attr, - data: { - value: attr?.data?.value.slice(0, 1) ?? [], - }, - }); - }), + attributes: newAttributes, }; }, undefined, ); }, [ - text, + allWidgets, leadId, - mappings, setValue, - filteredWidgets, - ]); - - const [draftEntryId, setDraftEntryId] = useState(undefined); - // FIXME: randomId is used to create different query variables after each poll - // so that apollo doesn't create unnecessary cache - const [randomId, setRandomId] = useState(randomString()); - const [predictionsLoading, setPredictionsLoading] = useState(false); - - const queryVariables = useMemo(() => ( - draftEntryId && projectId ? ({ - projectId, - draftEntryId, - randomId, - }) : undefined - ), [ - randomId, - projectId, - draftEntryId, + text, ]); - const [isErrored, setIsErrored] = useState(false); - const { loading: draftEntryFetchPending, data, @@ -519,24 +323,12 @@ function AssistItem(props: Props) { return; } - const validPredictions = result?.predictionTags?.filter(isDefined); - - /* - const geoPredictions = validPredictions?.map( - (prediction) => prediction.value, - ) ?? []; - */ - - const categoricalTags = validPredictions?.filter( - (prediction) => prediction.isSelected, - ).map( - (prediction) => prediction.tag, - ).filter(isDefined) ?? []; + const modelTags = result?.tags?.modelTags; + if (!isValidObject(modelTags)) { + return; + } - handleMappingsFetch({ - tags: categoricalTags, - locations: result.geoAreas?.filter(isDefined) ?? [], - }); + handleTagsFetch(modelTags as ModelTagsType); }, onError: () => { alert.show( @@ -604,10 +396,6 @@ function AssistItem(props: Props) { ]); const handleEntryCreateButtonClick = useCallback(() => { - if (!allRecommendations) { - return; - } - const submit = createSubmitHandler( validate, setError, @@ -646,7 +434,6 @@ function AssistItem(props: Props) { data, allWidgets, geoAreaOptions, - allRecommendations, validate, setError, onAssistedEntryAdd, @@ -735,12 +522,11 @@ function AssistItem(props: Props) { ; type Matrix2dWidgetAttribute = getType; +/* type ScaleWidgetAttribute = getType; type SingleSelectWidgetAttribute = getType; type MultiSelectWidgetAttribute = getType; type OrganigramWidgetAttribute = getType; type GeoLocationWidgetAttribute = getType; +*/ + +export function isValidObject(value: unknown): value is Record { + return typeof value === 'object' && !Array.isArray(value) && value !== null && value !== undefined; +} -export function createMatrix1dAttr( - mappings: Matrix1dMappingsItem[] | undefined, +export function isValidStringArray(value: unknown): value is string[] { + const isArray = typeof value === 'object' && Array.isArray(value) && value !== null && value !== undefined; + if (!isArray) { + return false; + } + return value.every((item) => typeof item === 'string'); +} + +// TODO: Write tests +export function createMatrix1dAttrFromTags( + unsafeMappings: Matrix1dValue | undefined, widget: Matrix1dWidget, ): Matrix1dWidgetAttribute | undefined { - if (!mappings || mappings.length <= 0) { + // NOTE: We are supposed to get Matrix1dValue, but adding one more validation layer + const mappings = unsafeMappings as unknown; + if (!mappings || !isValidObject(mappings)) { return undefined; } - const mappingsGroupedByRows = listToGroupList( - mappings, - (m) => m.association.rowKey, - (m) => m.association.subRowKey, + + const cellKeysByRowKey = listToMap( + widget.properties?.rows, + (row) => row.key, + (row) => row.cells.map((cell) => cell.key), ); - const value = mapToMap( - mappingsGroupedByRows, - (m) => m, - (d) => listToMap(d, (k) => k, () => true), + const rowKeys = widget.properties?.rows?.map((item) => item.key); + + const validRowKeys = Object.keys(mappings).filter((item) => rowKeys?.includes(item)); + + const value: Matrix1dValue = listToMap( + validRowKeys, + (validRowKey) => validRowKey, + (validRowKey) => { + const rowDetails = mappings[validRowKey]; + if (!isValidObject(rowDetails)) { + return undefined; + } + const cellKeys = Object.keys(rowDetails); + const validCellKeys = cellKeys.filter((item) => ( + cellKeysByRowKey?.[validRowKey].includes(item) + )); + + return listToMap(validCellKeys, (cellKey) => cellKey, () => true); + }, ); return { @@ -70,74 +87,76 @@ export function createMatrix1dAttr( }; } -interface SubRowMap { - type: 'SUB_ROW'; - rowKey: string; - subRowKey: string; -} - -type ColumnMap = { - type: 'COLUMN'; - columnKey: string; -} | { - type: 'SUB_COLUMN'; - columnKey: string; - subColumnKey: string; -}; - -export function filterColumn( - mappingsItem: Matrix2dMappingsItem, -): mappingsItem is DeepReplace { - return mappingsItem.association.type === 'COLUMN' || mappingsItem.association.type === 'SUB_COLUMN'; -} - -export function filterSubRows( - mappingsItem: Matrix2dMappingsItem, -): mappingsItem is DeepReplace { - return mappingsItem.association.type === 'SUB_ROW'; -} - -export function createMatrix2dAttr( - mappings: Matrix2dMappingsItem[] | undefined, +// TODO: Write tests +export function createMatrix2dAttrFromTags( + unsafeMappings: Matrix2dValue | undefined, widget: Matrix2dWidget, ): Matrix2dWidgetAttribute | undefined { - if (!mappings || mappings.length <= 0) { - return undefined; - } - const columns = mappings.filter(filterColumn); - const rows = mappings.filter(filterSubRows); - - if (columns.length === 0 || rows.length === 0) { + // NOTE: We are supposed to get Matrix1dValue, but adding one more validation layer + const mappings = unsafeMappings as unknown; + if (!mappings || !isValidObject(mappings)) { return undefined; } - const groupedCols = listToGroupList( - columns, - (col) => col.association.columnKey, - // NOTE: We need empty array for Columns while we need non-empty array for sub columns - (col) => (col.association.type === 'SUB_COLUMN' ? col.association.subColumnKey : undefined), + const subRowKeysByRowKey = listToMap( + widget.properties?.rows, + (row) => row.key, + (row) => row.subRows.map((subRow) => subRow.key), ); - const transformedCols = mapToMap( - groupedCols, - (colKey) => colKey, - (subColumns) => subColumns.filter(isDefined), + const subColumnKeysByColumnKey = listToMap( + widget.properties?.columns, + (row) => row.key, + (row) => row.subColumns.map((subColumn) => subColumn.key), ); - const groupedSubRows = listToGroupList( - rows, - (col) => col.association.rowKey, - (col) => col.association.subRowKey, - ); - - const value = mapToMap( - groupedSubRows, - (rowKey) => rowKey, - (subRows) => listToMap( - subRows, - (k) => k, - () => transformedCols, - ), + const rowKeys = widget.properties?.rows?.map((item) => item.key); + const columnKeys = widget.properties?.columns?.map((item) => item.key); + + const validRowKeys = Object.keys(mappings).filter((item) => rowKeys?.includes(item)); + + const value: Matrix2dValue = listToMap( + validRowKeys, + (validRowKey) => validRowKey, + (validRowKey) => { + const rowDetails = mappings[validRowKey]; + if (!isValidObject(rowDetails)) { + return undefined; + } + const subRowKeys = Object.keys(rowDetails); + const validsubRowKeys = subRowKeys.filter((item) => ( + subRowKeysByRowKey?.[validRowKey].includes(item) + )); + + return listToMap( + validsubRowKeys, + (subRowKey) => subRowKey, + (subRowKey) => { + const subRowData = rowDetails[subRowKey]; + if (!isValidObject(subRowData)) { + return undefined; + } + const unSafeColumnKeys = Object.keys(subRowData); + const validColumnKeys = unSafeColumnKeys.filter( + (item) => columnKeys?.includes(item), + ); + + return listToMap( + validColumnKeys, + (columnKey) => columnKey, + (columnKey) => { + const columnData = subRowData[columnKey]; + if (!isValidStringArray(columnData)) { + return undefined; + } + return columnData.filter( + (item) => subColumnKeysByColumnKey?.[columnKey].includes(item), + ); + }, + ); + }, + ); + }, ); return { @@ -148,131 +167,3 @@ export function createMatrix2dAttr( data: { value }, }; } - -export function createScaleAttr( - mappings: ScaleMappingsItem[] | undefined, - widget: ScaleWidget, -): { - attr: ScaleWidgetAttribute | undefined; - hints: string[] | undefined; -} { - if (!mappings || mappings.length <= 0) { - return { - attr: undefined, - hints: undefined, - }; - } - - if (mappings.length === 1) { - return { - attr: { - clientId: randomString(), - widget: widget.id, - widgetVersion: widget.version, - widgetType: 'SCALE' as const, - data: { - value: mappings[0].association.optionKey, - }, - }, - hints: mappings.map((m) => m.association.optionKey), - }; - } - - return ({ - attr: undefined, - hints: mappings.map((m) => m.association.optionKey), - }); -} - -export function createSelectAttr( - mappings: SelectMappingsItem[] | undefined, - widget: SingleSelectWidget, -): { - attr: SingleSelectWidgetAttribute | undefined; - hints: string[] | undefined; -} { - if (!mappings || mappings.length <= 0) { - return { - attr: undefined, - hints: undefined, - }; - } - - if (mappings.length === 1) { - return { - attr: { - clientId: randomString(), - widget: widget.id, - widgetVersion: widget.version, - widgetType: 'SELECT' as const, - data: { - value: mappings[0].association.optionKey, - }, - }, - hints: mappings.map((m) => m.association.optionKey), - }; - } - - return ({ - attr: undefined, - hints: mappings.map((m) => m.association.optionKey), - }); -} - -export function createMultiSelectAttr( - mappings: MultiSelectMappingsItem[] | undefined, - widget: MultiSelectWidget, -): MultiSelectWidgetAttribute | undefined { - if (!mappings || mappings.length <= 0) { - return undefined; - } - - return ({ - clientId: randomString(), - widget: widget.id, - widgetVersion: widget.version, - widgetType: 'MULTISELECT' as const, - data: { - value: mappings.map((m) => m.association.optionKey), - }, - }); -} - -export function createOrganigramAttr( - mappings: OrganigramMappingsItem[] | undefined, - widget: OrganigramWidget, -): OrganigramWidgetAttribute | undefined { - if (!mappings || mappings.length <= 0) { - return undefined; - } - - return ({ - clientId: randomString(), - widget: widget.id, - widgetVersion: widget.version, - widgetType: 'ORGANIGRAM' as const, - data: { - value: mappings.map((m) => m.association.optionKey), - }, - }); -} - -export function createGeoAttr( - locations: GeoArea[] | undefined, - widget: GeoLocationWidget, -): GeoLocationWidgetAttribute | undefined { - if (!locations || locations.length <= 0) { - return undefined; - } - - return ({ - clientId: randomString(), - widget: widget.id, - widgetVersion: widget.version, - widgetType: 'GEO' as const, - data: { - // NOTE: We are reducing the amount of suggestions from geo areas - value: locations.map((location) => location.id).slice(0, 3), - }, - }); -} diff --git a/app/components/LeftPaneEntries/AutoEntriesModal/index.tsx b/app/components/LeftPaneEntries/AutoEntriesModal/index.tsx index c2f48a3ec6..39aaa9411b 100644 --- a/app/components/LeftPaneEntries/AutoEntriesModal/index.tsx +++ b/app/components/LeftPaneEntries/AutoEntriesModal/index.tsx @@ -34,18 +34,6 @@ import { import { mergeLists } from '#utils/common'; import { type Framework } from '#components/entry/types'; import { type GeoArea } from '#components/GeoMultiSelectInput'; -import { - mappingsSupportedWidgets, - isCategoricalMappings, - WidgetHint, - filterMatrix1dMappings, - filterMatrix2dMappings, - filterScaleMappings, - filterSelectMappings, - filterMultiSelectMappings, - filterOrganigramMappings, - type MappingsItem, -} from '#types/newAnalyticalFramework'; import getSchema, { defaultFormValues, PartialEntryType, @@ -61,16 +49,17 @@ import { UpdateDraftEntryMutation, UpdateDraftEntryMutationVariables, } from '#generated/types'; +import { + ModelTagsType, + Matrix1dValue, + Matrix2dValue, +} from '#types/newAnalyticalFramework'; import AssistPopup from '../AssistItem/AssistPopup'; import { createDefaultAttributes } from '../utils'; import { - createOrganigramAttr, - createMatrix1dAttr, - createMatrix2dAttr, - createScaleAttr, - createSelectAttr, - createMultiSelectAttr, - createGeoAttr, + isValidObject, + createMatrix1dAttrFromTags, + createMatrix2dAttrFromTags, } from '../AssistItem/utils'; import styles from './styles.css'; @@ -103,17 +92,9 @@ const AUTO_ENTRIES_FOR_LEAD = gql` excerpt predictionReceivedAt predictionStatus - predictionTags { + tags { id - draftEntry - tag - dataTypeDisplay - dataType - category - isSelected - prediction - threshold - value + modelTags } geoAreas { adminLevelLevel @@ -184,190 +165,6 @@ const UPDATE_DRAFT_ENTRY = gql` } `; -interface EntryAttributes { - predictions: { - tags: string[]; - locations: GeoArea[]; - }; - mappings: MappingsItem[] | null | undefined; - filteredWidgets: NonNullable[number]['widgets'] - | NonNullable; -} - -function handleMappingsFetch(entryAttributes: EntryAttributes) { - const { - predictions, - mappings, - filteredWidgets, - } = entryAttributes; - - if (predictions.tags.length <= 0 && predictions.locations.length <= 0) { - // setMessageText('DEEP could not provide any recommendations for the selected text.'); - return {}; - } - - if (isNotDefined(filteredWidgets)) { - return {}; - } - - const matchedMappings = mappings - ?.filter(isCategoricalMappings) - .filter((m) => m.tag && predictions.tags.includes(m.tag)); - - const supportedGeoWidgets = mappings - ?.filter((mappingItem) => mappingItem.widgetType === 'GEO') - ?.map((mappingItem) => mappingItem.widget); - - const { - tempAttrs: recommendedAttributes, - tempHints: widgetsHints, - } = filteredWidgets.reduce( - ( - acc: { tempAttrs: PartialAttributeType[]; tempHints: WidgetHint[]; }, - widget, - ) => { - const { - tempAttrs: oldTempAttrs, - tempHints: oldTempHints, - } = acc; - - if (widget.widgetId === 'MATRIX1D') { - const supportedTags = matchedMappings - ?.filter((m) => m.widget === widget.id) - .filter(filterMatrix1dMappings); - - const attr = createMatrix1dAttr(supportedTags, widget); - return { - tempAttrs: attr ? [...oldTempAttrs, attr] : oldTempAttrs, - tempHints: oldTempHints, - }; - } - - if (widget.widgetId === 'MATRIX2D') { - const supportedTags = matchedMappings - ?.filter((m) => m.widget === widget.id) - .filter(filterMatrix2dMappings); - - const attr = createMatrix2dAttr(supportedTags, widget); - - return { - tempAttrs: attr ? [...oldTempAttrs, attr] : oldTempAttrs, - tempHints: oldTempHints, - }; - } - if (widget.widgetId === 'SCALE') { - const supportedTags = matchedMappings - ?.filter((m) => m.widget === widget.id) - .filter(filterScaleMappings); - - const { - attr, - hints, - } = createScaleAttr(supportedTags, widget); - - const hintsWithInfo: WidgetHint | undefined = hints ? { - widgetPk: widget.id, - widgetType: 'SCALE', - hints, - } : undefined; - - return { - tempAttrs: attr ? [...oldTempAttrs, attr] : oldTempAttrs, - tempHints: hintsWithInfo - ? [...oldTempHints, hintsWithInfo] - : oldTempHints, - }; - } - if (widget.widgetId === 'SELECT') { - const supportedTags = matchedMappings - ?.filter((m) => m.widget === widget.id) - .filter(filterSelectMappings); - - const { - attr, - hints, - } = createSelectAttr(supportedTags, widget); - - const hintsWithInfo: WidgetHint | undefined = hints ? { - widgetPk: widget.id, - widgetType: 'SELECT', - hints, - } : undefined; - - return { - tempAttrs: attr ? [...oldTempAttrs, attr] : oldTempAttrs, - tempHints: hintsWithInfo - ? [...oldTempHints, hintsWithInfo] - : oldTempHints, - }; - } - if (widget.widgetId === 'MULTISELECT') { - const supportedTags = matchedMappings - ?.filter((m) => m.widget === widget.id) - .filter(filterMultiSelectMappings); - - const attr = createMultiSelectAttr( - supportedTags, - widget, - ); - - return { - tempAttrs: attr ? [...oldTempAttrs, attr] : oldTempAttrs, - tempHints: oldTempHints, - }; - } - if (widget.widgetId === 'ORGANIGRAM') { - const supportedTags = matchedMappings - ?.filter((m) => m.widget === widget.id) - .filter(filterOrganigramMappings); - - const attr = createOrganigramAttr( - supportedTags, - widget, - ); - - return { - tempAttrs: attr ? [...oldTempAttrs, attr] : oldTempAttrs, - tempHints: oldTempHints, - }; - } - if ( - widget.widgetId === 'GEO' - && predictions.locations.length > 0 - && supportedGeoWidgets?.includes(widget.id) - ) { - const attr = createGeoAttr( - predictions.locations, - widget, - ); - - return { - tempAttrs: attr ? [...oldTempAttrs, attr] : oldTempAttrs, - tempHints: oldTempHints, - }; - } - return acc; - }, - { - tempAttrs: [], - tempHints: [], - }, - ); - - if (recommendedAttributes.length <= 0 && widgetsHints.length <= 0) { - // setMessageText( - // 'The provided recommendations for this text did not fit any tags in this project.', - // ); - return {}; - } - - return { - hints: widgetsHints, - recommendations: recommendedAttributes, - geoAreas: predictions.locations, - }; -} - const MAX_ITEMS_PER_PAGE = 20; const entryKeySelector = (entry: PartialEntryType) => entry.clientId; @@ -403,10 +200,12 @@ function AutoEntriesModal(props: Props) { ] = useState('extracted'); const [activePage, setActivePage] = useState(1); + const [recommendations, setRecommendations] = useState< + Record + >(); const { allWidgets, - filteredWidgets, } = useMemo(() => { const widgetsFromPrimary = frameworkDetails?.primaryTagging?.flatMap( (item) => (item.widgets ?? []), @@ -418,7 +217,6 @@ function AutoEntriesModal(props: Props) { ]; return { allWidgets: widgets, - filteredWidgets: widgets.filter((w) => mappingsSupportedWidgets.includes(w.widgetId)), }; }, [ frameworkDetails, @@ -566,17 +364,6 @@ function AutoEntriesModal(props: Props) { setRelevantEntries, ] = useState | undefined>(undefined); - const [ - allRecommendations, - setAllRecommendations, - ] = useState | undefined>(undefined); - - const [allHints, setAllHints] = useState< - Record | undefined - >(undefined); - - const mappings = frameworkDetails?.predictionTagsMapping; - const autoEntriesVariables = useMemo(() => ({ projectId, leadId, @@ -592,6 +379,26 @@ function AutoEntriesModal(props: Props) { activePage, ]); + const generateAttributesForEntry = useCallback((recommendedTags: Record) => { + const newAttributes = allWidgets?.map((widget) => { + if (widget.widgetId === 'MATRIX1D') { + return createMatrix1dAttrFromTags( + recommendedTags[widget.id] as Matrix1dValue, + widget, + ); + } + if (widget.widgetId === 'MATRIX2D') { + return createMatrix2dAttrFromTags( + recommendedTags[widget.id] as Matrix2dValue, + widget, + ); + } + return undefined; + }).filter(isDefined); + + return newAttributes; + }, [allWidgets]); + const { data: autoEntries, loading: autoEntriesLoading, @@ -608,27 +415,13 @@ function AutoEntriesModal(props: Props) { onCompleted: (response) => { const entries = response.project?.assistedTagging?.draftEntries?.results; const transformedEntries = (entries ?? [])?.map((entry) => { - const validPredictions = entry.predictionTags?.filter(isDefined); - const categoricalTags = validPredictions?.filter( - (prediction) => prediction.isSelected, - ).map( - (prediction) => prediction.tag, - ).filter(isDefined) ?? []; - - const entryAttributeData: EntryAttributes = { - predictions: { - tags: categoricalTags, - locations: entry.geoAreas?.filter(isDefined) ?? [], - }, - mappings, - filteredWidgets, - }; - - const { - hints: entryHints, - recommendations: entryRecommendations, - geoAreas: entryGeoAreas, - } = handleMappingsFetch(entryAttributeData); + const modelTags = entry?.tags?.modelTags; + if (!isValidObject(modelTags)) { + return undefined; + } + const entryRecommendations = generateAttributesForEntry( + modelTags as ModelTagsType, + ); const entryId = randomString(); const requiredEntry = { @@ -638,41 +431,23 @@ function AutoEntriesModal(props: Props) { excerpt: entry.excerpt, draftEntry: entry.id, droppedExcerpt: entry.excerpt, - attributes: entryRecommendations?.map((attr) => { - if (attr.widgetType !== 'GEO') { - return attr; - } - // NOTE: Selecting only the 1st recommendation - return ({ - ...attr, - data: { - value: attr?.data?.value.slice(0, 1) ?? [], - }, - }); - }), + attributes: entryRecommendations, }; return { entryId, - geoLocations: entryGeoAreas, - recommendations: entryRecommendations, - hints: entryHints, entry: requiredEntry, - relevant: !!entryHints || !!entryRecommendations || !!entryGeoAreas, + geoLocations: entry.geoAreas, + relevant: !!entryRecommendations, }; - }); + }).filter(isDefined); const requiredDraftEntries = transformedEntries?.map( (draftEntry) => draftEntry.entry, ); const entryRecommendations = listToMap( transformedEntries, (item) => item.entryId, - (item) => item.recommendations, - ); - const entryHints = listToMap( - transformedEntries, - (item) => item.entryId, - (item) => item.hints, + (item) => item.entry.attributes, ); const entryGeoAreas = listToMap( transformedEntries, @@ -687,9 +462,8 @@ function AutoEntriesModal(props: Props) { setValue({ entries: requiredDraftEntries, }); - setAllRecommendations(entryRecommendations); + setRecommendations(entryRecommendations); setRelevantEntries(tempRelevantEntries); - setAllHints(entryHints); setGeoAreaOptionsByEntryId(entryGeoAreas); setGeoAreaOptions(Object.values(entryGeoAreas).flat().filter(isDefined)); }, @@ -697,10 +471,6 @@ function AutoEntriesModal(props: Props) { ); const handleEntryCreateButtonClick = useCallback((entryId: string) => { - if (!allRecommendations?.[entryId]) { - return; - } - const selectedEntry = value?.entries?.find((item) => item.clientId === entryId); if (onAssistedEntryAdd && selectedEntry) { const duplicateEntryCheck = createdEntries?.find( @@ -759,7 +529,6 @@ function AutoEntriesModal(props: Props) { value?.entries, allWidgets, geoAreaOptionsByEntryId, - allRecommendations, onAssistedEntryAdd, createdEntries, ]); @@ -878,12 +647,11 @@ function AutoEntriesModal(props: Props) { frameworkDetails, value: datum, className: styles.listItem, + recommendations: recommendations?.[entryId], entryInputClassName: styles.entryInput, name: index, onChange: onEntryChange, leadId, - hints: allHints?.[entryId], - recommendations: allRecommendations?.[entryId], geoAreaOptions, onGeoAreaOptionsChange: setGeoAreaOptions, predictionsLoading: false, @@ -897,13 +665,12 @@ function AutoEntriesModal(props: Props) { relevant: relevantEntries?.[entryId], }); }, [ + recommendations, geoAreaOptions, relevantEntries, value?.entries, handleEntryCreateButtonClick, onEntryChange, - allHints, - allRecommendations, frameworkDetails, leadId, handleUpdateDraftEntryClick, diff --git a/app/components/LeftPaneEntries/SimplifiedTextView/index.tsx b/app/components/LeftPaneEntries/SimplifiedTextView/index.tsx index 545420a35e..824b777ff4 100644 --- a/app/components/LeftPaneEntries/SimplifiedTextView/index.tsx +++ b/app/components/LeftPaneEntries/SimplifiedTextView/index.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState, useCallback, useMemo } from 'react'; +import React, { useState, useCallback, useMemo } from 'react'; import { IoAdd } from 'react-icons/io5'; import { _cs, @@ -60,7 +60,6 @@ interface Props { disableApproveButton?: boolean; disableDiscardButton?: boolean; disableAddButton?: boolean; - assistedTaggingEnabled: boolean; projectId: string | undefined; frameworkDetails?: Framework; textZoomValue?: number | undefined; @@ -75,7 +74,6 @@ function SimplifiedTextView(props: Props) { onAssistedEntryAdd, onExcerptChange, activeEntryClientId, - assistedTaggingEnabled, onExcerptClick, onApproveButtonClick, leadId, @@ -96,15 +94,6 @@ function SimplifiedTextView(props: Props) { const [charactersLoaded, setCharactersLoaded] = useState(CHARACTER_PER_PAGE); const [textToAssist, setTextToAssist] = useState(); - useEffect(() => { - if (textToAssist && !assistedTaggingEnabled) { - setTextToAssist(undefined); - } - }, [ - textToAssist, - assistedTaggingEnabled, - ]); - const text = useMemo(() => { if (textFromProps) { const textLength = Math.min(textFromProps.length, charactersLoaded); @@ -360,46 +349,30 @@ function SimplifiedTextView(props: Props) { className={styles.actionsPopup} style={position ? ({ ...position }) : undefined} > - {assistedTaggingEnabled ? ( - <> - {isDefined(onAddButtonClick) && ( - - - - )} - {isDefined(onAssistedEntryAdd) && ( - - - - )} - - ) : ( - isDefined(onAddButtonClick) && ( - - - - ) + {isDefined(onAddButtonClick) && ( + + + + )} + {isDefined(onAssistedEntryAdd) && ( + + + )} )} diff --git a/app/components/LeftPaneEntries/index.tsx b/app/components/LeftPaneEntries/index.tsx index 307f393bd8..ea319a95ec 100644 --- a/app/components/LeftPaneEntries/index.tsx +++ b/app/components/LeftPaneEntries/index.tsx @@ -1,5 +1,4 @@ import React, { - useContext, useState, useMemo, useEffect, @@ -57,7 +56,6 @@ import { import { GeoArea } from '#components/GeoMultiSelectInput'; import LeadPreview from '#components/lead/LeadPreview'; import Screenshot from '#components/Screenshot'; -import { UserContext } from '#base/context/UserContext'; import { LeadPreviewForTextQuery, LeadPreviewForTextQueryVariables, @@ -208,8 +206,6 @@ function LeftPaneEntries(props: Props) { } = props; const alert = useAlert(); - const { user } = useContext(UserContext); - const entriesMappingByAttachment = useMemo(() => ( listToMap( entries?.map((entry) => { @@ -227,9 +223,6 @@ function LeftPaneEntries(props: Props) { ) ), [entries]); - const isAssistedTaggingAccessible = !!user - ?.accessibleFeatures?.some((feature) => feature.key === 'ASSISTED'); - const [activeTab, setActiveTab] = useState( (hideSimplifiedPreview && defaultTab === 'simplified') || (hideOriginalPreview && defaultTab === 'original') ? 'entries' @@ -718,12 +711,7 @@ function LeftPaneEntries(props: Props) { }); }, []); - const assistedTaggingShown = isAssistedTaggingAccessible - && frameworkDetails?.assistedTaggingEnabled - && (frameworkDetails?.predictionTagsMapping?.length ?? 0) > 0; - - const isAutoExtractionCompatible = isDefined(leadPreview?.textExtractionId) - && assistedTaggingShown; + const isAutoExtractionCompatible = isDefined(leadPreview?.textExtractionId); const errorMessageForAutoExtraction = useMemo(() => { if (isAutoExtractionCompatible) { @@ -786,7 +774,7 @@ function LeftPaneEntries(props: Props) { > <>
- {!isEntrySelectionActive && assistedTaggingShown && ( + {!isEntrySelectionActive && (
- ); -} - -export default CheckButton; diff --git a/app/views/AnalyticalFramework/AssistedTagging/CheckButton/styles.css b/app/views/AnalyticalFramework/AssistedTagging/CheckButton/styles.css deleted file mode 100644 index 120c2be339..0000000000 --- a/app/views/AnalyticalFramework/AssistedTagging/CheckButton/styles.css +++ /dev/null @@ -1,23 +0,0 @@ -.check-button { - position: relative; - overflow: visible; - text-transform: initial; - - .actions { - display: flex; - position: absolute; - top: -0.5em; - right: -0.5em; - align-items: center; - justify-content: center; - border: var(--dui-width-separator-thin) solid var(--color-nlp-tag); - /* NOTE: Want to move this a bit up */ - border-radius: 1em; - background-color: var(--dui-color-foreground); - padding: 2px; - min-width: 1.4em; - min-height: 1.4em; - color: var(--dui-color-nlp); - font-size: var(--dui-font-size-extra-small); - } -} diff --git a/app/views/AnalyticalFramework/AssistedTagging/FrameworkTagRow/AddButton/index.tsx b/app/views/AnalyticalFramework/AssistedTagging/FrameworkTagRow/AddButton/index.tsx deleted file mode 100644 index 8565b2f759..0000000000 --- a/app/views/AnalyticalFramework/AssistedTagging/FrameworkTagRow/AddButton/index.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import React, { useMemo, useCallback } from 'react'; -import { listToMap } from '@togglecorp/fujs'; -import { - QuickActionButton, - useBooleanState, - SelectInput, -} from '@the-deep/deep-ui'; -import { - IoCloseOutline, - IoAdd, -} from 'react-icons/io5'; - -import { - PredictionTag, - CategoricalMappingsItem, -} from '#types/newAnalyticalFramework'; - -const predictionKeySelector = (prediction: PredictionTag) => prediction.id; -const predictionLabelSelector = (prediction: PredictionTag) => prediction.name; -const predictionGroupKeySelector = (prediction: PredictionTag) => prediction.group ?? 'Misc'; - -interface Props { - disabled?: boolean; - existingMappings: CategoricalMappingsItem[] | undefined; - predictionTags: PredictionTag[] | undefined; - onTagSelect: (tag: string | undefined) => void; -} - -function AddFrameworkTagButton(props: Props) { - const { - disabled, - existingMappings, - predictionTags, - onTagSelect, - } = props; - - const [addInputShown, showAddInput, hideAddInput] = useBooleanState(false); - - const handleTagSelect = useCallback((newTag: string | undefined) => { - onTagSelect(newTag); - hideAddInput(); - }, [ - hideAddInput, - onTagSelect, - ]); - - const existingMappingsMap = useMemo(() => ( - listToMap( - existingMappings, - (d) => d.tag, - () => true, - ) - ), [existingMappings]); - - const filteredOptions = useMemo(() => ( - predictionTags?.filter( - (tag) => !existingMappingsMap?.[tag.id], - ) - ), [ - predictionTags, - existingMappingsMap, - ]); - - if (disabled) { - return null; - } - - if (addInputShown) { - return ( - <> - - - - - - ); - } - - return ( - - - - ); -} - -export default AddFrameworkTagButton; diff --git a/app/views/AnalyticalFramework/AssistedTagging/FrameworkTagRow/index.tsx b/app/views/AnalyticalFramework/AssistedTagging/FrameworkTagRow/index.tsx deleted file mode 100644 index 1814ca7d5d..0000000000 --- a/app/views/AnalyticalFramework/AssistedTagging/FrameworkTagRow/index.tsx +++ /dev/null @@ -1,145 +0,0 @@ -import React, { useMemo, useCallback } from 'react'; -import { - _cs, - listToMap, -} from '@togglecorp/fujs'; -import { - Tag, - List, - QuickActionButton, - DropContainer, -} from '@the-deep/deep-ui'; -import { - IoCloseOutline, -} from 'react-icons/io5'; - -import { - PredictionTag, - CategoricalMappingsItem, -} from '#types/newAnalyticalFramework'; - -import AddButton from './AddButton'; - -import styles from './styles.css'; - -const mappingKeySelector = (mapping: CategoricalMappingsItem) => mapping.tag; - -interface Props { - className?: string; - title: string; - itemKey: string; - onMappingRemoveClick: (cellKey: string, tagKey: string) => void; - onMappingAddClick: (cellKey: string, tagKey: string) => void; - associationKeySelector: (item: N) => string; - mappings: N[] | undefined; - predictionTags: PredictionTag[] | undefined; - disabled?: boolean; -} - -function FrameworkTagRow(props: Props) { - const { - className, - title, - itemKey, - mappings, - predictionTags, - onMappingRemoveClick, - onMappingAddClick, - disabled, - associationKeySelector, - } = props; - - const predictionTagsById = useMemo(() => ( - listToMap( - predictionTags, - (tag) => tag.id, - (tag) => tag, - ) - ), [predictionTags]); - - const handleTagRemove = useCallback((tagKey: string) => { - onMappingRemoveClick(itemKey, tagKey); - }, [ - itemKey, - onMappingRemoveClick, - ]); - - const tagRendererParams = useCallback((tagKey: string) => ({ - className: styles.tag, - children: predictionTagsById?.[tagKey]?.name, - actions: ( - - - - ), - variant: 'accent' as const, - }), [ - predictionTagsById, - handleTagRemove, - ]); - - const filteredMappings = useMemo(() => ( - mappings?.filter((mapping) => associationKeySelector(mapping) === itemKey) - ), [ - mappings, - itemKey, - associationKeySelector, - ]); - - const handleDrop = useCallback((valFromArgument: Record | undefined) => { - const val = valFromArgument as { badgeKey: string | undefined }; - if (!val || !val.badgeKey || typeof val.badgeKey !== 'string') { - return; - } - onMappingAddClick(itemKey, val.badgeKey); - }, [ - itemKey, - onMappingAddClick, - ]); - - const handleTagSelect = useCallback((newTag: string | undefined) => { - if (newTag) { - onMappingAddClick(itemKey, newTag); - } - }, [ - onMappingAddClick, - itemKey, - ]); - - return ( -
-
- - {title} - -
- - - - -
- ); -} - -export default FrameworkTagRow; diff --git a/app/views/AnalyticalFramework/AssistedTagging/FrameworkTagRow/styles.css b/app/views/AnalyticalFramework/AssistedTagging/FrameworkTagRow/styles.css deleted file mode 100644 index a704285c8f..0000000000 --- a/app/views/AnalyticalFramework/AssistedTagging/FrameworkTagRow/styles.css +++ /dev/null @@ -1,38 +0,0 @@ -.framework-tag-row { - display: flex; - border-bottom: var(--dui-width-separator-thin) solid var(--dui-color-separator); - - .left-container { - display: flex; - align-items: center; - flex-shrink: 0; - border-right: var(--dui-width-separator-thin) solid var(--dui-color-separator); - padding: var(--dui-spacing-medium) var(--dui-spacing-small); - width: 40%; - } - - .drop-container { - display: flex; - flex-shrink: 0; - flex-wrap: wrap; - background: unset; - width: 60%; - gap: var(--dui-spacing-extra-small); - - .right-container { - display: flex; - align-items: center; - flex-wrap: wrap; - padding: var(--dui-spacing-medium) var(--dui-spacing-small); - gap: var(--dui-spacing-extra-small); - - .tag { - background-color: var(--dui-color-nlp); - - .remove-button { - padding: 0; - } - } - } - } -} diff --git a/app/views/AnalyticalFramework/AssistedTagging/TagWithBadge/index.tsx b/app/views/AnalyticalFramework/AssistedTagging/TagWithBadge/index.tsx deleted file mode 100644 index 11adab74ab..0000000000 --- a/app/views/AnalyticalFramework/AssistedTagging/TagWithBadge/index.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import React, { useMemo } from 'react'; -import { _cs } from '@togglecorp/fujs'; - -import { - Tag, - DraggableContent, -} from '@the-deep/deep-ui'; - -import styles from './styles.css'; - -interface Props { - className?: string; - children: React.ReactNode; - badgeCount?: number; - badgeKey: string; -} - -function TagWithBadge(props: Props) { - const { - className, - children, - badgeCount = 0, - badgeKey, - } = props; - - const dragValue = useMemo(() => ({ - badgeKey, - }), [badgeKey]); - - return ( - 0 && styles.nlp, - )} - actionsContainerClassName={styles.actions} - actions={badgeCount > 0 && badgeCount} - spacing="none" - > - - {children} - - - ); -} - -export default TagWithBadge; diff --git a/app/views/AnalyticalFramework/AssistedTagging/TagWithBadge/styles.css b/app/views/AnalyticalFramework/AssistedTagging/TagWithBadge/styles.css deleted file mode 100644 index a94cee58a8..0000000000 --- a/app/views/AnalyticalFramework/AssistedTagging/TagWithBadge/styles.css +++ /dev/null @@ -1,53 +0,0 @@ -.check-button { - position: relative; - background-color: var(--dui-color-background-negative); - overflow: visible; - text-transform: initial; - - .drag-container { - display: flex; - align-items: center; - flex-direction: row; - border: unset; - background: transparent; - padding: var(--dui-spacing-small) var(--dui-spacing-large); - text-align: left; - gap: 0; - - .header { - display: flex; - padding: 0; - - .heading { - padding: 0; - min-width: 0; - } - >* { - gap: 0; - } - } - } - - .actions { - display: flex; - position: absolute; - top: -0.5em; - right: -0.5em; - align-items: center; - justify-content: center; - border: var(--dui-width-separator-thin) solid var(--color-nlp-tag); - /* NOTE: Want to move this a bit up */ - border-radius: 1em; - background-color: var(--dui-color-foreground); - padding: 2px; - min-width: 1.4em; - min-height: 1.4em; - color: var(--dui-color-nlp); - font-size: var(--dui-font-size-extra-small); - } - - &.nlp { - background-color: var(--dui-color-nlp); - color: var(--dui-color-text-on-accent); - } -} diff --git a/app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/Matrix1dTagInput/index.tsx b/app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/Matrix1dTagInput/index.tsx deleted file mode 100644 index 1f09767cd8..0000000000 --- a/app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/Matrix1dTagInput/index.tsx +++ /dev/null @@ -1,183 +0,0 @@ -import React, { useMemo, useCallback } from 'react'; -import { - _cs, - unique, - listToMap, - isDefined, - randomString, -} from '@togglecorp/fujs'; -import { - ListView, -} from '@the-deep/deep-ui'; - -import { - Matrix1dWidget, - Matrix1dMappingsItem, - PredictionTag, -} from '#types/newAnalyticalFramework'; -import { sortByOrder } from '#utils/common'; - -import CellGroup from '../../CellGroup'; -import FrameworkTagRow from '../../FrameworkTagRow'; - -import styles from './styles.css'; - -const matrix1dAssociationKeySelector = ( - mapping: Matrix1dMappingsItem, -) => mapping.association.subRowKey; - -interface CellItem { - subRowKey: string; - subRowLabel: string; - rowOrder: number; - subRowOrder: number; - rowKey: string; - rowLabel: string; -} - -const cellKeySelector = (cell: CellItem) => cell.subRowKey; -const groupKeySelector = (n: CellItem) => n.rowKey; - -interface Props { - className?: string; - widget: Matrix1dWidget; - mappings: Matrix1dMappingsItem[] | undefined; - onMappingsChange: (newMappings: Matrix1dMappingsItem[], widgetPk: string) => void; - disabled?: boolean; - predictionTags: PredictionTag[] | undefined; -} - -function Matrix1dTagInput(props: Props) { - const { - className, - widget, - mappings, - onMappingsChange, - disabled, - predictionTags, - } = props; - - const sortedCells = useMemo(() => ( - sortByOrder(widget?.properties?.rows) - ?.map((row) => ( - row.cells.map((cell) => ({ - rowKey: row.key, - rowOrder: row.order, - rowLabel: row.label, - subRowKey: cell.key, - subRowOrder: cell.order, - subRowLabel: cell.label, - })) - )) - .flat() - ), [widget.properties?.rows]); - - const groupLabelMap = useMemo(() => ( - listToMap( - unique(sortedCells ?? [], (cell) => cell.rowKey), - (cell) => cell.rowKey, - (cell) => cell.rowLabel, - ) - ), [sortedCells]); - - const handleCellRemove = useCallback((cellKey: string, tagKey: string) => { - if (!mappings) { - return; - } - const selectedMappingsIndex = mappings.findIndex((mapping) => ( - tagKey === mapping.tag && mapping.association.subRowKey === cellKey - )); - - if (selectedMappingsIndex !== -1) { - const newMappings = [...mappings]; - newMappings.splice(selectedMappingsIndex, 1); - - onMappingsChange(newMappings, widget.id); - } - }, [ - onMappingsChange, - widget.id, - mappings, - ]); - - const handleCellAdd = useCallback((cellKey: string, tagKey: string) => { - const selectedMappingsIndex = mappings?.findIndex((mapping) => ( - tagKey === mapping.tag && mapping.association.subRowKey === cellKey - )); - - if (isDefined(selectedMappingsIndex) && selectedMappingsIndex !== -1) { - return; - } - - const rowKey = sortedCells - ?.find((cell) => cell.subRowKey === cellKey)?.rowKey; - - if (!rowKey) { - // eslint-disable-next-line no-console - console.error('Sub-row without row is found'); - return; - } - - onMappingsChange([ - ...(mappings ?? []), - { - tag: tagKey, - widget: widget.id, - widgetType: 'MATRIX1D', - association: { - subRowKey: cellKey, - rowKey, - }, - clientId: randomString(), - // FIXME: need to cast here because we cannot set id - // and a proper fix would require more time - } as Matrix1dMappingsItem, - ], widget.id); - }, [ - sortedCells, - onMappingsChange, - mappings, - widget, - ]); - - const tagRowRendererParams = useCallback((_: string, cell: CellItem) => ({ - title: cell.subRowLabel, - itemKey: cell.subRowKey, - onMappingRemoveClick: handleCellRemove, - onMappingAddClick: handleCellAdd, - associationKeySelector: matrix1dAssociationKeySelector, - mappings, - predictionTags, - disabled, - }), [ - disabled, - handleCellRemove, - handleCellAdd, - mappings, - predictionTags, - ]); - - const subRowGroupRendererParams = useCallback((groupKey: string) => ({ - title: groupLabelMap[groupKey] ?? groupKey, - direction: 'vertical' as const, - }), [groupLabelMap]); - - return ( - - ); -} - -export default Matrix1dTagInput; diff --git a/app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/Matrix1dTagInput/styles.css b/app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/Matrix1dTagInput/styles.css deleted file mode 100644 index fb68193c19..0000000000 --- a/app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/Matrix1dTagInput/styles.css +++ /dev/null @@ -1,4 +0,0 @@ -.matrix-tag-input { - display: flex; - flex-direction: column; -} diff --git a/app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/Matrix2dTagInput/index.tsx b/app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/Matrix2dTagInput/index.tsx deleted file mode 100644 index e6a6a372b4..0000000000 --- a/app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/Matrix2dTagInput/index.tsx +++ /dev/null @@ -1,465 +0,0 @@ -import React, { useMemo, useCallback } from 'react'; -import { - _cs, - listToMap, - unique, - isDefined, - randomString, -} from '@togglecorp/fujs'; -import { - List, - ListView, -} from '@the-deep/deep-ui'; - -import { - Matrix2dWidget, - Matrix2dMappingsItem, - PredictionTag, -} from '#types/newAnalyticalFramework'; -import { sortByOrder } from '#utils/common'; -import { getType } from '#utils/types'; - -import FrameworkTagRow from '../../FrameworkTagRow'; -import CellGroup from '../../CellGroup'; - -import styles from './styles.css'; - -type ColMappingItem = Omit & { - association: getType; -}; - -type SubRowMappingItem = Omit & { - association: getType; -}; - -type SubColumnMappingItem = Omit & { - association: getType; -}; - -const columnAssociationKeySelector = (item: ColMappingItem) => item.association.columnKey; -const subRowAssociationKeySelector = (item: SubRowMappingItem) => item.association.subRowKey; -const subColumnAssociationKeySelector = ( - item: SubColumnMappingItem, -) => item.association.subColumnKey; - -interface ColItem { - columnKey: string; - label: string; -} - -const colKeySelector = (c: ColItem) => c.columnKey; - -interface SubRowItem { - subRowKey: string; - subRowLabel: string; - rowOrder: number; - subRowOrder: number; - rowKey: string; - rowLabel: string; -} - -interface SubColumnItem { - columnKey: string; - columnLabel: string; - columnOrder: number; - subColumnKey: string; - subColumnLabel: string; - subColumnOrder: number; -} - -const subRowKeySelector = (subRow: SubRowItem) => subRow.subRowKey; -const subRowGroupKeySelector = (item: SubRowItem) => item.rowKey; - -const subColumnKeySelector = (subRow: SubColumnItem) => subRow.subColumnKey; -const subColumnGroupKeySelector = (item: SubColumnItem) => item.columnKey; - -interface Props { - className?: string; - widget: Matrix2dWidget; - mappings: Matrix2dMappingsItem[] | undefined; - onMappingsChange: (newMappings: Matrix2dMappingsItem[], widgetPk: string) => void; - disabled?: boolean; - predictionTags: PredictionTag[] | undefined; -} - -function Matrix2dTagInput(props: Props) { - const { - className, - widget, - mappings, - onMappingsChange, - disabled, - predictionTags, - } = props; - - const sortedColumns = useMemo(() => ( - sortByOrder(widget?.properties?.columns)?.map((column) => ({ - columnKey: column.key, - label: column.label, - })) - ), [widget.properties?.columns]); - - const sortedSubRows = useMemo(() => ( - sortByOrder(widget?.properties?.rows) - ?.map((row) => ( - row.subRows.map((cell) => ({ - rowKey: row.key, - rowOrder: row.order, - rowLabel: row.label, - subRowKey: cell.key, - subRowOrder: cell.order, - subRowLabel: cell.label, - })) - )) - .flat() - ), [widget.properties?.rows]); - - const sortedSubColumns = useMemo(() => ( - sortByOrder(widget?.properties?.columns) - ?.map((column) => ( - column.subColumns.map((cell) => ({ - columnKey: column.key, - columnOrder: column.order, - columnLabel: column.label, - subColumnKey: cell.key, - subColumnOrder: cell.order, - subColumnLabel: cell.label, - })) - )) - .flat() - ), [widget.properties?.columns]); - - const subRowMappings = useMemo(() => ( - mappings?.filter((mappingItem): mappingItem is SubRowMappingItem => ( - mappingItem.association.type === 'SUB_ROW' - )) - ), [ - mappings, - ]); - - const subColumnMappings = useMemo(() => ( - mappings?.filter((mappingItem): mappingItem is SubColumnMappingItem => ( - mappingItem.association.type === 'SUB_COLUMN' - )) - ), [ - mappings, - ]); - - const columnMappings = useMemo(() => ( - mappings?.filter((mappingItem): mappingItem is ColMappingItem => ( - mappingItem.association.type === 'COLUMN' - )) - ), [ - mappings, - ]); - - const subColumnGroupLabelMap = useMemo(() => ( - listToMap( - unique(sortedSubColumns ?? [], (cell) => cell.columnKey), - (cell) => cell.columnKey, - (cell) => cell.columnLabel, - ) - ), [sortedSubColumns]); - - const subRowGroupLabelMap = useMemo(() => ( - listToMap( - unique(sortedSubRows ?? [], (cell) => cell.rowKey), - (cell) => cell.rowKey, - (cell) => cell.rowLabel, - ) - ), [sortedSubRows]); - - const handleColumnRemove = useCallback((columnKey: string, tagKey: string) => { - if (!mappings) { - return; - } - const selectedMappingsIndex = mappings.findIndex((mapping) => ( - tagKey === mapping.tag - && mapping.association.type === 'COLUMN' - && mapping.association.columnKey === columnKey - )); - - if (selectedMappingsIndex !== -1) { - const newMappings = [...mappings]; - newMappings.splice(selectedMappingsIndex, 1); - - onMappingsChange(newMappings, widget.id); - } - }, [ - onMappingsChange, - widget.id, - mappings, - ]); - - const handleColumnAdd = useCallback((columnKey: string, tagKey: string) => { - const selectedMappingsIndex = mappings?.findIndex((mapping) => ( - tagKey === mapping.tag - && mapping.association.type === 'COLUMN' - && mapping.association.columnKey === columnKey - )); - - if (isDefined(selectedMappingsIndex) && selectedMappingsIndex !== -1) { - return; - } - onMappingsChange([ - ...(mappings ?? []), - { - tag: tagKey, - widget: widget.id, - widgetType: widget.widgetId, - association: { - type: 'COLUMN', - columnKey, - }, - clientId: randomString(), - // FIXME: need to cast here because we cannot set id - // and a proper fix would require more time - } as Matrix2dMappingsItem, - ], widget.id); - }, [ - onMappingsChange, - widget, - mappings, - ]); - - const handleSubRowRemove = useCallback((subRowKey: string, tagKey: string) => { - if (!mappings) { - return; - } - - const selectedMappingsIndex = mappings.findIndex((mapping) => ( - tagKey === mapping.tag - && mapping.association.type === 'SUB_ROW' - && mapping.association.subRowKey === subRowKey - )); - - if (selectedMappingsIndex !== -1) { - const newMappings = [...mappings]; - newMappings.splice(selectedMappingsIndex, 1); - - onMappingsChange(newMappings, widget.id); - } - }, [ - onMappingsChange, - widget.id, - mappings, - ]); - - const handleSubRowAdd = useCallback((subRowKey: string, tagKey: string) => { - const selectedMappingsIndex = mappings?.findIndex((mapping) => ( - tagKey === mapping.tag - && mapping.association.type === 'SUB_ROW' - && mapping.association.subRowKey === subRowKey - )); - - if (isDefined(selectedMappingsIndex) && selectedMappingsIndex !== -1) { - return; - } - const rowKey = sortedSubRows - ?.find((subRow) => subRow.subRowKey === subRowKey)?.rowKey; - - if (!rowKey) { - // eslint-disable-next-line no-console - console.error('Sub-row without row is found'); - return; - } - - const newMappings = [ - ...(mappings ?? []), - { - tag: tagKey, - widget: widget.id, - widgetType: 'MATRIX2D', - association: { - type: 'SUB_ROW', - subRowKey, - rowKey, - }, - clientId: randomString(), - // FIXME: need to cast here because we cannot set id - // and a proper fix would require more time - } as Matrix2dMappingsItem, - ]; - onMappingsChange(newMappings, widget.id); - }, [ - sortedSubRows, - onMappingsChange, - mappings, - widget, - ]); - - const handleSubColumnRemove = useCallback((subColumnKey: string, tagKey: string) => { - if (!mappings) { - return; - } - const selectedMappingsIndex = mappings.findIndex((mapping) => ( - tagKey === mapping.tag - && mapping.association.type === 'SUB_COLUMN' - && mapping.association.subColumnKey === subColumnKey - )); - - if (selectedMappingsIndex !== -1) { - const newMappings = [...mappings]; - newMappings.splice(selectedMappingsIndex, 1); - - onMappingsChange(newMappings, widget.id); - } - }, [ - onMappingsChange, - widget.id, - mappings, - ]); - - const handleSubColumnAdd = useCallback((subColumnKey: string, tagKey: string) => { - const selectedMappingsIndex = mappings?.findIndex((mapping) => ( - tagKey === mapping.tag - && mapping.association.type === 'SUB_COLUMN' - && mapping.association.subColumnKey === subColumnKey - )); - - if (isDefined(selectedMappingsIndex) && selectedMappingsIndex !== -1) { - return; - } - - const columnKey = sortedSubColumns?.find( - (subColumn) => subColumn.subColumnKey === subColumnKey, - )?.columnKey; - - if (!columnKey) { - // eslint-disable-next-line no-console - console.error('Sub-column without column is found'); - return; - } - - const newMappings = [ - ...(mappings ?? []), - { - tag: tagKey, - widget: widget.id, - widgetType: 'MATRIX2D', - association: { - type: 'SUB_COLUMN', - subColumnKey, - columnKey, - }, - clientId: randomString(), - // FIXME: need to cast here because we cannot set id - // and a proper fix would require more time - } as Matrix2dMappingsItem, - ]; - - onMappingsChange(newMappings, widget.id); - }, [ - sortedSubColumns, - onMappingsChange, - mappings, - widget, - ]); - - const colRendererParams = useCallback((_: string, column: ColItem) => ({ - title: column.label, - itemKey: column.columnKey, - onMappingRemoveClick: handleColumnRemove, - onMappingAddClick: handleColumnAdd, - mappings: columnMappings, - associationKeySelector: columnAssociationKeySelector, - predictionTags, - disabled, - }), [ - disabled, - handleColumnAdd, - handleColumnRemove, - columnMappings, - predictionTags, - ]); - - const subRowRendererParams = useCallback((_: string, subRow: SubRowItem) => ({ - title: subRow.subRowLabel, - itemKey: subRow.subRowKey, - onMappingRemoveClick: handleSubRowRemove, - onMappingAddClick: handleSubRowAdd, - mappings: subRowMappings, - associationKeySelector: subRowAssociationKeySelector, - predictionTags, - disabled, - }), [ - disabled, - handleSubRowRemove, - handleSubRowAdd, - subRowMappings, - predictionTags, - ]); - - const subColumnRendererParams = useCallback((_: string, subColumn: SubColumnItem) => ({ - title: subColumn.subColumnLabel, - itemKey: subColumn.subColumnKey, - onMappingRemoveClick: handleSubColumnRemove, - onMappingAddClick: handleSubColumnAdd, - mappings: subColumnMappings, - associationKeySelector: subColumnAssociationKeySelector, - predictionTags, - disabled, - }), [ - disabled, - handleSubColumnRemove, - handleSubColumnAdd, - subColumnMappings, - predictionTags, - ]); - - const subColumnGroupRendererParams = useCallback((groupKey: string) => ({ - title: subColumnGroupLabelMap[groupKey] ?? groupKey, - direction: 'vertical' as const, - }), [subColumnGroupLabelMap]); - - const subRowGroupRendererParams = useCallback((groupKey: string) => ({ - title: subRowGroupLabelMap[groupKey] ?? groupKey, - direction: 'vertical' as const, - }), [subRowGroupLabelMap]); - - return ( -
- - - - - -
- ); -} - -export default Matrix2dTagInput; diff --git a/app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/Matrix2dTagInput/styles.css b/app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/Matrix2dTagInput/styles.css deleted file mode 100644 index 56e436bbe4..0000000000 --- a/app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/Matrix2dTagInput/styles.css +++ /dev/null @@ -1,16 +0,0 @@ -.matrix-tag-input { - display: flex; - flex-direction: column; - - .sub-columns { - display: flex; - flex-direction: column; - gap: var(--dui-spacing-small); - } - - .sub-rows { - display: flex; - flex-direction: column; - gap: var(--dui-spacing-small); - } -} diff --git a/app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/OptionTypeTagInput/index.tsx b/app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/OptionTypeTagInput/index.tsx deleted file mode 100644 index eda817a36e..0000000000 --- a/app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/OptionTypeTagInput/index.tsx +++ /dev/null @@ -1,135 +0,0 @@ -import React, { useMemo, useCallback } from 'react'; -import { - isDefined, - randomString, -} from '@togglecorp/fujs'; -import { - ListView, -} from '@the-deep/deep-ui'; - -import { - ScaleWidget, - SingleSelectWidget, - MultiSelectWidget, - ScaleMappingsItem, - SelectMappingsItem, - MultiSelectMappingsItem, - KeyLabelEntity, - PredictionTag, -} from '#types/newAnalyticalFramework'; -import { sortByOrder } from '#utils/common'; - -import FrameworkTagRow from '../../FrameworkTagRow'; - -const cellKeySelector = (cell: KeyLabelEntity) => cell.key; -const optionTypeKeySelector = ( - mapping: ScaleMappingsItem | SelectMappingsItem | MultiSelectMappingsItem, -) => mapping.association.optionKey; - -interface Props { - className?: string; - widget: ScaleWidget | SingleSelectWidget | MultiSelectWidget; - mappings: (ScaleMappingsItem | SelectMappingsItem | MultiSelectMappingsItem)[] | undefined; - onMappingsChange: ( - newMappings: (ScaleMappingsItem | SelectMappingsItem | MultiSelectMappingsItem)[], - widgetPk: string, - ) => void; - predictionTags: PredictionTag[] | undefined; - disabled?: boolean; -} - -function OptionTypeTagInput(props: Props) { - const { - className, - widget, - mappings, - onMappingsChange, - predictionTags, - disabled, - } = props; - - const sortedCells = useMemo(() => ( - sortByOrder(widget?.properties?.options) ?? [] - ), [widget.properties?.options]); - - const handleCellRemove = useCallback((cellKey: string, tagKey: string) => { - if (!mappings) { - return; - } - const selectedMappingsIndex = mappings.findIndex((mapping) => ( - tagKey === mapping.tag && mapping.association.optionKey === cellKey - )); - - if (selectedMappingsIndex !== -1) { - const newMappings = [...mappings]; - newMappings.splice(selectedMappingsIndex, 1); - - onMappingsChange(newMappings, widget.id); - } - }, [ - onMappingsChange, - widget.id, - mappings, - ]); - const handleCellAdd = useCallback((cellKey: string, tagKey: string) => { - const selectedMappingsIndex = mappings?.findIndex((mapping) => ( - tagKey === mapping.tag - && mapping.association.optionKey === cellKey - )); - - if (isDefined(selectedMappingsIndex) && selectedMappingsIndex !== -1) { - return; - } - - onMappingsChange([ - ...(mappings ?? []), - { - tag: tagKey, - widget: widget.id, - widgetType: widget.widgetId, - association: { - optionKey: cellKey, - }, - clientId: randomString(), - // FIXME: need to cast here because we cannot set id - // and a proper fix would require more time - } as ScaleMappingsItem | SelectMappingsItem | MultiSelectMappingsItem, - ], widget.id); - }, [ - onMappingsChange, - mappings, - widget, - ]); - - const cellRendererParams = useCallback((_: string, cell: KeyLabelEntity) => ({ - title: cell.label, - itemKey: cell.key, - onMappingRemoveClick: handleCellRemove, - onMappingAddClick: handleCellAdd, - associationKeySelector: optionTypeKeySelector, - mappings, - predictionTags, - disabled, - }), [ - disabled, - mappings, - handleCellRemove, - handleCellAdd, - predictionTags, - ]); - - return ( - - ); -} - -export default OptionTypeTagInput; diff --git a/app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/OrganigramTagInput/index.tsx b/app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/OrganigramTagInput/index.tsx deleted file mode 100644 index 335af328e4..0000000000 --- a/app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/OrganigramTagInput/index.tsx +++ /dev/null @@ -1,132 +0,0 @@ -import React, { useMemo, useCallback } from 'react'; -import { - isDefined, - randomString, -} from '@togglecorp/fujs'; -import { - ListView, -} from '@the-deep/deep-ui'; - -import { - OrganigramWidget, - OrganigramMappingsItem, - KeyLabelEntity, - PredictionTag, -} from '#types/newAnalyticalFramework'; -import { sortByOrder } from '#utils/common'; - -import FrameworkTagRow from '../../FrameworkTagRow'; - -import { getOrganigramFlatOptions } from './utils'; - -const cellKeySelector = (cell: KeyLabelEntity) => cell.key; -const optionTypeKeySelector = (mapping: OrganigramMappingsItem) => mapping.association.optionKey; - -interface Props { - className?: string; - widget: OrganigramWidget; - mappings: OrganigramMappingsItem[] | undefined; - onMappingsChange: ( - newMappings: OrganigramMappingsItem[], - widgetPk: string, - ) => void; - predictionTags: PredictionTag[] | undefined; - disabled?: boolean; -} - -function OrganigramTagInput(props: Props) { - const { - className, - widget, - mappings, - onMappingsChange, - disabled, - predictionTags, - } = props; - - const sortedCells = useMemo(() => ( - sortByOrder(getOrganigramFlatOptions(widget?.properties?.options)) ?? [] - ), [widget.properties?.options]); - - const handleCellRemove = useCallback((cellKey: string, tagKey: string) => { - if (!mappings) { - return; - } - const selectedMappingsIndex = mappings.findIndex((mapping) => ( - tagKey === mapping.tag && mapping.association.optionKey === cellKey - )); - - if (selectedMappingsIndex !== -1) { - const newMappings = [...mappings]; - newMappings.splice(selectedMappingsIndex, 1); - - onMappingsChange(newMappings, widget.id); - } - }, [ - onMappingsChange, - widget.id, - mappings, - ]); - - const handleCellAdd = useCallback((cellKey: string, tagKey: string) => { - const selectedMappingsIndex = mappings?.findIndex((mapping) => ( - tagKey === mapping.tag - && mapping.association.optionKey === cellKey - )); - - if (isDefined(selectedMappingsIndex) && selectedMappingsIndex !== -1) { - return; - } - - onMappingsChange([ - ...(mappings ?? []), - { - tag: tagKey, - widget: widget.id, - widgetType: widget.widgetId, - association: { - optionKey: cellKey, - }, - clientId: randomString(), - // FIXME: need to cast here because we cannot set id - // and a proper fix would require more time - } as OrganigramMappingsItem, - ], widget.id); - }, [ - onMappingsChange, - mappings, - widget, - ]); - - const cellRendererParams = useCallback((_: string, cell: KeyLabelEntity) => ({ - title: cell.label, - itemKey: cell.key, - onMappingRemoveClick: handleCellRemove, - onMappingAddClick: handleCellAdd, - associationKeySelector: optionTypeKeySelector, - mappings, - predictionTags, - disabled, - }), [ - predictionTags, - mappings, - disabled, - handleCellAdd, - handleCellRemove, - ]); - - return ( - - ); -} - -export default OrganigramTagInput; diff --git a/app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/OrganigramTagInput/utils.ts b/app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/OrganigramTagInput/utils.ts deleted file mode 100644 index 96d382b533..0000000000 --- a/app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/OrganigramTagInput/utils.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { OrganigramDatum } from '#types/newAnalyticalFramework'; - -// FIXME: move this to utils -// eslint-disable-next-line import/prefer-default-export -export function getOrganigramFlatOptions( - data?: OrganigramDatum, - prefix?: string, -): Omit[] { - if (!data) { - return []; - } - const { children, ...values } = data; - const label = `${prefix ? `${prefix}/` : ''}${values.label}`; - - const childrenValues = children?.flatMap((v) => getOrganigramFlatOptions(v, label)) ?? []; - return [ - { ...values, label }, - ...childrenValues, - ]; -} diff --git a/app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/index.tsx b/app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/index.tsx deleted file mode 100644 index 64bba3ad75..0000000000 --- a/app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/index.tsx +++ /dev/null @@ -1,150 +0,0 @@ -import React, { useMemo } from 'react'; -import { _cs } from '@togglecorp/fujs'; -import { Container } from '@the-deep/deep-ui'; - -import { - Widget, - PredictionTag, - CategoricalMappingsItem, - Matrix1dMappingsItem, - Matrix2dMappingsItem, - ScaleMappingsItem, - SelectMappingsItem, - OrganigramMappingsItem, - MultiSelectMappingsItem, -} from '#types/newAnalyticalFramework'; - -import Matrix1dTagInput from './Matrix1dTagInput'; -import Matrix2dTagInput from './Matrix2dTagInput'; -import OptionTypeTagInput from './OptionTypeTagInput'; -import OrganigramTagInput from './OrganigramTagInput'; - -import styles from './styles.css'; - -interface Props { - className?: string; - widget: Widget; - predictionTags: PredictionTag[] | undefined; - mappings: CategoricalMappingsItem[] | undefined; - onMappingsChange: (newMappings: CategoricalMappingsItem[], widgetPk: string) => void; - disabled?: boolean; -} - -function WidgetTagList(props: Props) { - const { - className, - widget, - mappings, - onMappingsChange, - disabled, - predictionTags, - } = props; - - const filteredMappings = useMemo( - () => mappings?.filter((mappingItem) => ( - mappingItem.widgetType === widget.widgetId - && mappingItem.widget === widget.id - )), - [ - mappings, - widget, - ], - ); - - if (widget.widgetId === 'MATRIX1D') { - return ( - - - - ); - } - - if (widget.widgetId === 'MATRIX2D') { - return ( - - - - ); - } - - if ( - widget.widgetId === 'SCALE' - || widget.widgetId === 'SELECT' - || widget.widgetId === 'MULTISELECT' - ) { - return ( - - - - ); - } - - if (widget.widgetId === 'ORGANIGRAM') { - return ( - - - - ); - } - - return ( - - This widget is not supported at the moment. - - ); -} - -export default WidgetTagList; diff --git a/app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/styles.css b/app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/styles.css deleted file mode 100644 index c797f4a8d8..0000000000 --- a/app/views/AnalyticalFramework/AssistedTagging/WidgetTagList/styles.css +++ /dev/null @@ -1,9 +0,0 @@ -.widget-tag-list { - display: flex; - flex-direction: column; - background-color: transparent; - - .content { - overflow: visible; - } -} diff --git a/app/views/AnalyticalFramework/AssistedTagging/index.tsx b/app/views/AnalyticalFramework/AssistedTagging/index.tsx deleted file mode 100644 index deaa6ac128..0000000000 --- a/app/views/AnalyticalFramework/AssistedTagging/index.tsx +++ /dev/null @@ -1,445 +0,0 @@ -import React, { useCallback, useMemo } from 'react'; -import { - _cs, - listToMap, - listToGroupList, - isDefined, - randomString, - unique, -} from '@togglecorp/fujs'; -import { - Error, - SetValueArg, - analyzeErrors, -} from '@togglecorp/toggle-form'; -import { compareTwoStrings } from 'string-similarity'; -import { - ConfirmButton, - ListView, - useAlert, - Card, - Container, - ContainerCard, - Header, - Switch, - Link, -} from '@the-deep/deep-ui'; - -import { - Widget, - MappingsItem, - isCategoricalMappings, - categoricalWidgets, - GeoMappingsItem, -} from '#types/newAnalyticalFramework'; -import { - AssistedPredictionTagsQuery, -} from '#generated/types'; -import ProgressLine from '#components/ProgressLine'; -import generateString from '#utils/string'; - -import { - getMatrix2dPossibleMappings, - getMatrix1dPossibleMappings, - getOptionTypePossibleMappings, - getOrganigramPossibleMappings, -} from './utils'; -import { WidgetsType } from '../schema'; -import TagWithBadge from './TagWithBadge'; -import CheckButton from './CheckButton'; -import WidgetTagList from './WidgetTagList'; -import CellGroup from './CellGroup'; - -import styles from './styles.css'; - -function isCaseInsensitiveMatch(foo: string | undefined = '', bar: string | undefined = '') { - return (compareTwoStrings(foo?.toLowerCase(), bar?.toLowerCase()) > 0.7); -} - -type AssistedTag = NonNullable['assistedTagging']>['predictionTags']>[number]; - -const nlpLabelGroupKeySelector = (tag: AssistedTag) => tag.group ?? 'Miscellaneous'; -const nlpLabelKeySelector = (tag: AssistedTag) => tag.id; -const widgetKeySelector = (widget: Widget) => widget.clientId; - -interface Props { - className?: string; - allWidgets: WidgetsType | undefined; - assistedTaggingEnabled: boolean | undefined; - onAssistedTaggingStatusChange: (newVal: boolean) => void; - pending?: boolean; - assistedPredictionTags: AssistedTag[] | undefined | null; - - name: K; - value: MappingsItem[] | undefined; - error: Error | undefined; - onChange: (value: SetValueArg, name: K) => void; - disabled?: boolean; -} - -function AssistedTagging(props: Props) { - const { - className, - allWidgets, - assistedTaggingEnabled, - pending, - onAssistedTaggingStatusChange, - assistedPredictionTags, - name, - value: mappings, - error, - onChange, - disabled, - } = props; - const alert = useAlert(); - - const errored = analyzeErrors(error); - - const predictionTags = useMemo(() => ( - assistedPredictionTags?.filter((tag) => ( - !tag.isCategory && !tag.hideInAnalysisFrameworkMapping - )) - ), [assistedPredictionTags]); - - type SetMappingsFn = React.Dispatch>; - const setMappings = useCallback((newMappings) => { - onChange(newMappings, name); - }, [ - onChange, - name, - ]); - - const widgets = useMemo(() => ( - allWidgets - ?.filter((widget) => ( - isDefined(widget.id) && categoricalWidgets.includes(widget.widgetId) - )) - ), [allWidgets]); - - const geoWidgets = useMemo(() => ( - allWidgets?.filter((widget) => isDefined(widget.id) && widget.widgetId === 'GEO') - ), [allWidgets]); - - const categoricalMappings = useMemo( - () => mappings?.filter(isCategoricalMappings), - [mappings], - ); - - const mappingsByTagId = useMemo(() => ( - listToGroupList( - categoricalMappings, - (mappingItem) => mappingItem.tag, - ) - ), [categoricalMappings]); - - const geoWidgetsMappingValue = useMemo(() => ( - listToMap( - mappings?.filter((mapping) => mapping.widgetType === 'GEO'), - (mapping) => mapping.widget, - () => true, - ) - ), [mappings]); - - const possibleTagsInFramework = useMemo(() => ( - widgets?.map((widget) => { - if (widget.widgetId === 'MATRIX2D') { - return getMatrix2dPossibleMappings(widget); - } - if (widget.widgetId === 'MATRIX1D') { - return getMatrix1dPossibleMappings(widget); - } - if ( - widget.widgetId === 'SCALE' - || widget.widgetId === 'SELECT' - || widget.widgetId === 'MULTISELECT' - ) { - return getOptionTypePossibleMappings(widget); - } - if (widget.widgetId === 'ORGANIGRAM') { - return getOrganigramPossibleMappings(widget); - } - return []; - }).flat() - ), [widgets]); - - const handleGeoWidgetClick = useCallback((widgetPk: string) => { - setMappings((oldMappings = []) => { - const selectedWidgetIndex = oldMappings.findIndex( - (mapping) => mapping.widget === widgetPk, - ); - if (selectedWidgetIndex !== -1) { - return oldMappings.filter((mapping) => mapping.widget !== widgetPk); - } - return [ - ...oldMappings, - { - widgetType: 'GEO', - widget: widgetPk, - clientId: randomString(), - // FIXME: need to cast here because we cannot set id - // and a proper fix would require more time - } as GeoMappingsItem, - ]; - }); - }, [setMappings]); - - const handleWidgetMappingsChange = useCallback(( - newWidgetMappings: MappingsItem[], - widgetPk: string, - ) => { - setMappings((oldMappings = []) => { - const filteredMappings = oldMappings.filter( - (mapping) => mapping.widget !== widgetPk, - ); - const finalMappings = [ - ...filteredMappings, - ...newWidgetMappings, - ]; - return finalMappings; - }); - }, [setMappings]); - - const nlpLabelGroupRendererParams = useCallback((groupKey: string) => ({ - title: groupKey, - }), []); - - const geoWidgetsRendererParams = useCallback((itemKey: string, widget: Widget) => ({ - children: widget.title, - name: itemKey, - value: !!geoWidgetsMappingValue?.[widget.id], - onClick: handleGeoWidgetClick, - disabled: disabled || !assistedTaggingEnabled, - }), [ - assistedTaggingEnabled, - disabled, - geoWidgetsMappingValue, - handleGeoWidgetClick, - ]); - - const widgetRendererParams = useCallback((_: string, widget: Widget) => ({ - widget, - mappings: categoricalMappings, - onMappingsChange: handleWidgetMappingsChange, - predictionTags, - disabled: disabled || !assistedTaggingEnabled, - }), [ - predictionTags, - disabled, - assistedTaggingEnabled, - categoricalMappings, - handleWidgetMappingsChange, - ]); - - const uniqueMappedPercent = useMemo(() => { - if (!categoricalMappings || !possibleTagsInFramework) { - return 0; - } - const uniqueMappings = unique( - categoricalMappings, - (map) => JSON.stringify(map?.association), - ).length; - return Math.round((uniqueMappings / possibleTagsInFramework.length) * 10000) / 100; - }, [ - categoricalMappings, - possibleTagsInFramework, - ]); - - const handleAutoMatchClick = useCallback(() => { - if (!possibleTagsInFramework) { - return; - } - const mapping = possibleTagsInFramework.map((item) => ( - predictionTags?.reduce((acc, tag) => { - if (!isCaseInsensitiveMatch(item?.label, tag.name)) { - return acc; - } - return ([ - { - ...item, - tag: tag.id, - clientId: randomString(), - }, - ...acc, - ]); - }, [] as Omit[]) - )).flat().filter(isDefined); - - // FIXME: MappingsItem requires id, but its not required at first - setMappings(mapping as MappingsItem[]); - const newMappings = (mapping ?? []) as MappingsItem[]; - const uniqueMappings = unique( - newMappings.filter(isCategoricalMappings), - (map) => JSON.stringify(map?.association), - ).length; - const newMappedPercent = Math.round( - (uniqueMappings / possibleTagsInFramework.length) * 10000, - ) / 100; - - alert.show( - `DEEP was able to match ${newMappedPercent}% tags in your framework. Please, verify the matches and add the ones that are missing.`, - { - variant: 'success', - duration: 6000, - }, - ); - }, [ - alert, - possibleTagsInFramework, - setMappings, - predictionTags, - ]); - - const viewOnlyNlpRendererParams = useCallback((itemKey: string, tag: AssistedTag) => ({ - children: tag.name, - badgeCount: mappingsByTagId?.[itemKey]?.length ?? 0, - badgeKey: tag.id, - }), [ - mappingsByTagId, - ]); - - return ( -
-
- this video - - ), - }, - )} - actionsContainerClassName={styles.actions} - actions={( - <> - - - - )} - /> -
- - -
- My Framework -
-
- Auto Match - - )} - /> -
- )} - > - - - - - - - {(geoWidgets?.length ?? 0) > 0 && ( - - - - - - )} -
-
- ); -} - -export default AssistedTagging; diff --git a/app/views/AnalyticalFramework/AssistedTagging/styles.css b/app/views/AnalyticalFramework/AssistedTagging/styles.css deleted file mode 100644 index 090b05b59a..0000000000 --- a/app/views/AnalyticalFramework/AssistedTagging/styles.css +++ /dev/null @@ -1,106 +0,0 @@ -.assisted-tagging { - display: flex; - flex-direction: column; - flex-grow: 1; - background-color: var(--dui-color-background); - overflow-y: auto; - gap: var(--dui-spacing-medium); - - .header { - flex-shrink: 0; - background-color: var(--dui-color-foreground); - padding: var(--dui-spacing-large) var(--dui-spacing-super-large); - gap: var(--dui-spacing-large); - - .actions { - display: flex; - align-items: flex-end; - flex-direction: column; - flex-shrink: 0; - justify-content: flex-end; - - .progress-line { - width: 16rem; - - .title { - font-weight: var(--dui-font-weight-bold); - } - } - } - } - - .link { - display: inline-flex; - - .link-actions { - display: none; - } - } - - .content { - display: flex; - flex-direction: column; - flex-grow: 1; - margin: var(--dui-spacing-medium) var(--dui-spacing-super-large); - gap: var(--dui-spacing-medium); - overflow-y: auto; - - .card { - display: flex; - overflow-y: auto; - gap: var(--dui-spacing-medium); - - .heading-container { - width: 100%; - - .heading { - display: flex; - - .left-container { - padding: var(--dui-spacing-small); - width: 40%; - } - - .right-container { - padding: var(--dui-spacing-small); - width: 60%; - } - } - } - - .nlp-framework-list, - .selected-framework-list { - overflow-y: auto; - } - } - - .geo-widgets { - flex-shrink: 0; - } - - .current-framework { - display: flex; - flex-basis: calc(70% - var(--dui-spacing-medium) / 2); - flex-shrink: 0; - background-color: var(--dui-color-foreground); - } - - .nlp-framework { - display: flex; - flex-basis: calc(30% - var(--dui-spacing-medium) / 2); - flex-shrink: 0; - background-color: var(--dui-color-background-information); - } - - &.fade-out { - filter: grayscale(50%); - opacity: 0.4; - } - } - - .geo-widget-list { - display: flex; - flex-wrap: wrap; - gap: var(--dui-spacing-small); - } -} diff --git a/app/views/AnalyticalFramework/AssistedTagging/utils.test.ts b/app/views/AnalyticalFramework/AssistedTagging/utils.test.ts deleted file mode 100644 index 69896ae938..0000000000 --- a/app/views/AnalyticalFramework/AssistedTagging/utils.test.ts +++ /dev/null @@ -1,419 +0,0 @@ -import { - Matrix2dWidget, - Matrix1dWidget, - ScaleWidget, - OrganigramWidget, -} from '#types/newAnalyticalFramework'; - -import { - getMatrix2dPossibleMappings, - getMatrix1dPossibleMappings, - getOptionTypePossibleMappings, - Matrix1dPossibleMapping, - Matrix2dPossibleMapping, - ScalePossibleMapping, - OrganigramPossibleMapping, - getOrganigramPossibleMappings, -} from './utils'; - -const matrix2dSample: Matrix2dWidget = { - width: 'FULL', - version: 11, - title: 'Matrix 2D', - order: 11, - key: 'sample-matrix-2d', - id: '12', - clientId: 'sample-matrix-2d', - widgetId: 'MATRIX2D', - properties: { - columns: [ - { - order: 1, - key: 'column-1', - label: 'Column 1', - subColumns: [ - { - order: 1, - key: 'sub-col-1', - label: 'Sub Col 1', - }, - { - order: 2, - key: 'sub-col-2', - label: 'Sub Col 2', - }, - ], - }, - { - order: 2, - key: 'column-2', - label: 'Column 2', - subColumns: [], - }, - ], - rows: [ - { - order: 1, - color: 'red', - key: 'row-1', - label: 'Row 1', - subRows: [ - { - key: 'sub-row-1-1', - label: 'Sub Row 1 1', - order: 1, - }, - { - key: 'sub-row-1-2', - label: 'Sub Row 1 2', - order: 2, - }, - ], - }, - { - order: 2, - color: 'red', - key: 'row-2', - label: 'Row 2', - subRows: [ - { - key: 'sub-row-2-1', - label: 'Sub Row 2 1', - order: 1, - }, - { - key: 'sub-row-2-2', - label: 'Sub Row 2 2', - order: 2, - }, - { - key: 'sub-row-2-3', - label: 'Sub Row 2 3', - order: 3, - }, - ], - }, - ], - }, -}; - -const matrix2dPossibleMapping: Matrix2dPossibleMapping[] = [ - { - label: 'Column 1', - widget: '12', - widgetType: 'MATRIX2D', - association: { - type: 'COLUMN', - columnKey: 'column-1', - }, - }, - { - label: 'Column 2', - widget: '12', - widgetType: 'MATRIX2D', - association: { - type: 'COLUMN', - columnKey: 'column-2', - }, - }, - { - label: 'Sub Col 1', - widget: '12', - widgetType: 'MATRIX2D', - association: { - type: 'SUB_COLUMN', - columnKey: 'column-1', - subColumnKey: 'sub-col-1', - }, - }, - { - label: 'Sub Col 2', - widget: '12', - widgetType: 'MATRIX2D', - association: { - type: 'SUB_COLUMN', - columnKey: 'column-1', - subColumnKey: 'sub-col-2', - }, - }, - { - label: 'Sub Row 1 1', - widget: '12', - widgetType: 'MATRIX2D', - association: { - type: 'SUB_ROW', - rowKey: 'row-1', - subRowKey: 'sub-row-1-1', - }, - }, - { - label: 'Sub Row 1 2', - widget: '12', - widgetType: 'MATRIX2D', - association: { - type: 'SUB_ROW', - rowKey: 'row-1', - subRowKey: 'sub-row-1-2', - }, - }, - { - label: 'Sub Row 2 1', - widget: '12', - widgetType: 'MATRIX2D', - association: { - type: 'SUB_ROW', - rowKey: 'row-2', - subRowKey: 'sub-row-2-1', - }, - }, - { - label: 'Sub Row 2 2', - widget: '12', - widgetType: 'MATRIX2D', - association: { - type: 'SUB_ROW', - rowKey: 'row-2', - subRowKey: 'sub-row-2-2', - }, - }, - { - label: 'Sub Row 2 3', - widget: '12', - widgetType: 'MATRIX2D', - association: { - type: 'SUB_ROW', - rowKey: 'row-2', - subRowKey: 'sub-row-2-3', - }, - }, -]; - -const matrix1dSample: Matrix1dWidget = { - width: 'FULL', - version: 11, - title: 'Matrix 1D', - order: 11, - key: 'sample-matrix-1d', - id: '13', - clientId: 'sample-matrix-1d', - widgetId: 'MATRIX1D', - properties: { - rows: [ - { - key: 'row-1', - label: 'Row 1', - order: 1, - color: '#b0b0b0', - cells: [ - { - key: 'sub-row-1-1', - label: 'Sub Row 1 1', - order: 1, - }, - ], - }, - { - key: 'row-2', - label: 'row-2', - order: 2, - color: '#ff0000', - cells: [ - { - key: 'sub-row-2-1', - label: 'Sub Row 2 1', - order: 1, - }, - { - key: 'sub-row-2-2', - label: 'Sub Row 2 2', - order: 2, - }, - ], - }, - ], - }, -}; - -const matrix1dPossibleMapping: Matrix1dPossibleMapping[] = [ - { - label: 'Sub Row 1 1', - widget: '13', - widgetType: 'MATRIX1D', - association: { - rowKey: 'row-1', - subRowKey: 'sub-row-1-1', - }, - }, - { - label: 'Sub Row 2 1', - widget: '13', - widgetType: 'MATRIX1D', - association: { - rowKey: 'row-2', - subRowKey: 'sub-row-2-1', - }, - }, - { - label: 'Sub Row 2 2', - widget: '13', - widgetType: 'MATRIX1D', - association: { - rowKey: 'row-2', - subRowKey: 'sub-row-2-2', - }, - }, -]; - -const scaleSample: ScaleWidget = { - width: 'HALF', - version: 11, - title: 'Scale', - order: 11, - key: 'sample-scale', - id: '14', - clientId: 'sample-scale', - widgetId: 'SCALE', - properties: { - options: [ - { - key: 'option-1', - label: 'Option 1', - order: 1, - color: '#f0f0f0', - }, - { - key: 'option-2', - label: 'Option 2', - order: 2, - color: '#123123', - }, - ], - }, -}; - -const scalePossibleMapping: ScalePossibleMapping[] = [ - { - label: 'Option 1', - widget: '14', - widgetType: 'SCALE', - association: { - optionKey: 'option-1', - }, - }, - { - label: 'Option 2', - widget: '14', - widgetType: 'SCALE', - association: { - optionKey: 'option-2', - }, - }, -]; - -const organigramSample: OrganigramWidget = { - width: 'HALF', - version: 11, - title: 'Organigram', - order: 11, - key: 'sample-organigram', - id: '15', - clientId: 'sample-organigram', - widgetId: 'ORGANIGRAM', - properties: { - options: { - key: 'parent-0', - label: 'Parent 0', - order: 1, - children: [ - { - key: 'child-0-1', - label: 'Child 0 1', - order: 1, - children: [ - { - key: 'child-1-1', - label: 'Child 1 1', - order: 1, - children: [], - }, - { - key: 'child-1-2', - label: 'Child 1 2', - order: 2, - children: [], - }, - ], - }, - { - key: 'child-0-2', - label: 'Child 0 2', - order: 2, - children: [], - }, - ], - }, - }, -}; - -const organigramPossibleMapping: OrganigramPossibleMapping[] = [ - { - label: 'Parent 0', - widget: '15', - widgetType: 'ORGANIGRAM', - association: { - optionKey: 'parent-0', - }, - }, - { - label: 'Parent 0/Child 0 1', - widget: '15', - widgetType: 'ORGANIGRAM', - association: { - optionKey: 'child-0-1', - }, - }, - { - label: 'Parent 0/Child 0 1/Child 1 1', - widget: '15', - widgetType: 'ORGANIGRAM', - association: { - optionKey: 'child-1-1', - }, - }, - { - label: 'Parent 0/Child 0 1/Child 1 2', - widget: '15', - widgetType: 'ORGANIGRAM', - association: { - optionKey: 'child-1-2', - }, - }, - { - label: 'Parent 0/Child 0 2', - widget: '15', - widgetType: 'ORGANIGRAM', - association: { - optionKey: 'child-0-2', - }, - }, -]; - - -test('get matrix possible mappings', () => { - expect(getMatrix2dPossibleMappings(undefined)).toStrictEqual([]); - expect( - getMatrix2dPossibleMappings(matrix2dSample), - ).toStrictEqual(matrix2dPossibleMapping); - expect(getMatrix1dPossibleMappings(undefined)).toStrictEqual([]); - expect( - getMatrix1dPossibleMappings(matrix1dSample), - ).toStrictEqual(matrix1dPossibleMapping); - expect(getOptionTypePossibleMappings(undefined)).toStrictEqual([]); - expect( - getOptionTypePossibleMappings(scaleSample), - ).toStrictEqual(scalePossibleMapping); - expect(getOrganigramPossibleMappings(undefined)).toStrictEqual([]); - expect( - getOrganigramPossibleMappings(organigramSample), - ).toStrictEqual(organigramPossibleMapping); -}); diff --git a/app/views/AnalyticalFramework/AssistedTagging/utils.ts b/app/views/AnalyticalFramework/AssistedTagging/utils.ts deleted file mode 100644 index 8bd8e8ebf3..0000000000 --- a/app/views/AnalyticalFramework/AssistedTagging/utils.ts +++ /dev/null @@ -1,128 +0,0 @@ -import { - Matrix2dWidget, - Matrix2dMappingsItem, - Matrix1dWidget, - Matrix1dMappingsItem, - ScaleWidget, - SingleSelectWidget, - MultiSelectWidget, - ScaleMappingsItem, - SelectMappingsItem, - MultiSelectMappingsItem, - OrganigramWidget, - OrganigramMappingsItem, -} from '#types/newAnalyticalFramework'; - -import { getOrganigramFlatOptions } from './WidgetTagList/OrganigramTagInput/utils'; - -type ConvertToPossibleMapping = Omit & { label: string }; - -export type Matrix2dPossibleMapping = ConvertToPossibleMapping; -export type Matrix1dPossibleMapping = ConvertToPossibleMapping; -export type ScalePossibleMapping = ConvertToPossibleMapping; -export type SelectPossibleMapping = ConvertToPossibleMapping; -export type MultiSelectPossibleMapping = ConvertToPossibleMapping; -export type OrganigramPossibleMapping = ConvertToPossibleMapping; - -export function getMatrix2dPossibleMappings( - widget: Matrix2dWidget | undefined, -): Matrix2dPossibleMapping[] { - const columns = widget?.properties?.columns?.map((column): Matrix2dPossibleMapping => ({ - label: column.label, - widget: widget.id, - widgetType: 'MATRIX2D' as const, - association: { - type: 'COLUMN' as const, - columnKey: column.key, - }, - })) ?? []; - - const subColumns: Matrix2dPossibleMapping[] = widget?.properties?.columns - ?.map((column) => ( - column.subColumns.map((cell) => ({ - label: cell.label, - widget: widget.id, - widgetType: 'MATRIX2D' as const, - association: { - type: 'SUB_COLUMN' as const, - subColumnKey: cell.key, - columnKey: column.key, - }, - })) - )).flat() ?? []; - - const subRows: Matrix2dPossibleMapping[] = widget?.properties?.rows - ?.map((row) => ( - row.subRows.map((cell) => ({ - label: cell.label, - widget: widget.id, - widgetType: 'MATRIX2D' as const, - association: { - type: 'SUB_ROW' as const, - subRowKey: cell.key, - rowKey: row.key, - }, - })) - )).flat() ?? []; - - return [ - ...columns, - ...subColumns, - ...subRows, - ]; -} - -export function getMatrix1dPossibleMappings( - widget: Matrix1dWidget | undefined, -): Matrix1dPossibleMapping[] { - const subRows = widget?.properties?.rows - ?.map((row) => ( - row.cells.map((cell) => ({ - label: cell.label, - widget: widget.id, - widgetType: 'MATRIX1D' as const, - association: { - subRowKey: cell.key, - rowKey: row.key, - }, - })) - )).flat() ?? []; - - return subRows; -} - -export function getOptionTypePossibleMappings( - widget: ScaleWidget | MultiSelectWidget | SingleSelectWidget | undefined, -): (ScalePossibleMapping | SelectPossibleMapping | MultiSelectPossibleMapping)[] { - const subRows = widget?.properties?.options - ?.map((option) => ({ - label: option.label, - widget: widget.id, - widgetType: widget.widgetId, - association: { - optionKey: option.key, - }, - })) ?? []; - - return subRows; -} - -export function getOrganigramPossibleMappings( - widget: OrganigramWidget | undefined, -): OrganigramPossibleMapping[] { - if (!widget) { - return []; - } - const flatOptions = getOrganigramFlatOptions(widget.properties?.options) ?? []; - const options = flatOptions - ?.map((option) => ({ - label: option.label, - widget: widget.id, - widgetType: 'ORGANIGRAM' as const, - association: { - optionKey: option.key, - }, - })) ?? []; - - return options; -} diff --git a/app/views/AnalyticalFramework/index.tsx b/app/views/AnalyticalFramework/index.tsx index 6e26eae071..e4e8a1ead0 100644 --- a/app/views/AnalyticalFramework/index.tsx +++ b/app/views/AnalyticalFramework/index.tsx @@ -2,7 +2,6 @@ import React, { useMemo, useState, useCallback, useEffect, useContext } from 're import { _cs, isDefined, - listToGroupList, } from '@togglecorp/fujs'; import { useParams, @@ -52,13 +51,8 @@ import NewOrganizationSelectInput, { BasicOrganization } from '#components/selec import PreloadMessage from '#base/components/PreloadMessage'; import FullPageErrorMessage from '#views/FullPageErrorMessage'; import SubNavbarContext from '#components/SubNavbar/context'; -import { - MappingsItem, - categoricalWidgets, -} from '#types/newAnalyticalFramework'; import _ts from '#ts'; import { - AssistedPredictionTagsQuery, CurrentFrameworkQuery, CurrentFrameworkQueryVariables, UpdateFrameworkMutation, @@ -82,14 +76,7 @@ import { CURRENT_FRAMEWORK, UPDATE_FRAMEWORK, CREATE_FRAMEWORK, - ASSISTED_PREDICTION_TAGS_QUERY, } from './queries'; -import { - getMatrix2dPossibleMappings, - getMatrix1dPossibleMappings, - getOptionTypePossibleMappings, - getOrganigramPossibleMappings, -} from './AssistedTagging/utils'; import PrivacyInput from './components/PrivacyInput'; import UserTable from './UserTable'; import UploadImage from './UploadImage'; @@ -97,118 +84,8 @@ import PrimaryTagging from './PrimaryTagging'; import Properties from './Properties'; import SecondaryTagging from './SecondaryTagging'; import Review from './Review'; -import AssistedTagging from './AssistedTagging'; import styles from './styles.css'; -const stripGhostAssociations = ( - newWidgets: WidgetsType | undefined, - currentMapping: MappingsItem[] | undefined, -) => { - const newCatWidgets = newWidgets - ?.filter((widget) => ( - isDefined(widget.id) && categoricalWidgets.includes(widget.widgetId) - )); - const newGeoWidgets = newWidgets - ?.filter((widget) => ( - isDefined(widget.id) && widget.widgetId === 'GEO' - )); - const possibleTagsInFramework = listToGroupList( - newCatWidgets?.map((widget) => { - if (widget.widgetId === 'MATRIX2D') { - return getMatrix2dPossibleMappings(widget); - } - if (widget.widgetId === 'MATRIX1D') { - return getMatrix1dPossibleMappings(widget); - } - if ( - widget.widgetId === 'SCALE' - || widget.widgetId === 'SELECT' - || widget.widgetId === 'MULTISELECT' - ) { - return getOptionTypePossibleMappings(widget); - } - if (widget.widgetId === 'ORGANIGRAM') { - return getOrganigramPossibleMappings(widget); - } - return []; - }).flat(), - (item) => item.widget, - (item) => item, - ); - - const filteredMappings = currentMapping?.filter( - (item) => { - const possibleAssociations = possibleTagsInFramework?.[item.widget]; - if (item.widgetType === 'MATRIX1D') { - return possibleAssociations?.some((possibleTag) => { - if (possibleTag.widgetType === 'MATRIX1D') { - return ( - possibleTag.association.rowKey === item.association.rowKey - && possibleTag.association.subRowKey === item.association.subRowKey - ); - } - return false; - }); - } - if (item.widgetType === 'MATRIX2D') { - return possibleAssociations?.some((possibleTag) => { - if (possibleTag.widgetType === 'MATRIX2D') { - return ( - item.association.type === 'SUB_ROW' && possibleTag.association.type === 'SUB_ROW' - && possibleTag.association.rowKey === item.association.rowKey - && possibleTag.association.subRowKey === item.association.subRowKey - ) || ( - item.association.type === 'SUB_COLUMN' && possibleTag.association.type === 'SUB_COLUMN' - && possibleTag.association.columnKey === item.association.columnKey - && possibleTag - .association.subColumnKey === item.association.subColumnKey - ) || ( - item.association.type === 'COLUMN' && possibleTag.association.type === 'COLUMN' - && possibleTag.association.columnKey === item.association.columnKey - ); - } - return false; - }); - } - if ( - item.widgetType === 'SCALE' - || item.widgetType === 'SELECT' - || item.widgetType === 'MULTISELECT' - ) { - return possibleAssociations?.some((possibleTag) => { - if ( - possibleTag.widgetType === 'SCALE' - || possibleTag.widgetType === 'SELECT' - || possibleTag.widgetType === 'MULTISELECT' - ) { - return ( - possibleTag.association.optionKey === item.association.optionKey - ); - } - return false; - }); - } - if (item.widgetType === 'ORGANIGRAM') { - return possibleAssociations?.some((possibleTag) => { - if (possibleTag.widgetType === 'ORGANIGRAM') { - return ( - possibleTag.association.optionKey === item.association.optionKey - ); - } - return false; - }); - } - if (item.widgetType === 'GEO') { - return newGeoWidgets?.some( - (possibleTag) => item.widget === possibleTag.id, - ); - } - return false; - }, - ); - return filteredMappings; -}; - function getTimestamp(dateString: string | undefined) { if (!dateString) { return 0; @@ -226,7 +103,6 @@ function transformFramework(framework: Framework): FrameworkInput { secondaryTagging, properties, modifiedAt, - predictionTagsMapping, assistedTaggingEnabled, } = framework; @@ -237,7 +113,6 @@ function transformFramework(framework: Framework): FrameworkInput { organization: organization?.id, primaryTagging, secondaryTagging, - predictionTagsMapping, assistedTaggingEnabled, properties, isVisualizationEnabled: isDefined(properties), @@ -250,7 +125,6 @@ interface Stored { pristine: boolean, primaryTaggingPristine: boolean, secondaryTaggingPristine: boolean, - predictionTagsMappingPristine: boolean, framework: PartialFormType, frameworkImage: Framework['previewImage'], organizationOptions: BasicOrganization[] | null | undefined, @@ -292,9 +166,6 @@ function AnalyticalFramework(props: Props) { const [actionsNode, setActionsNode] = useState(); const [iconsNode, setIconsNode] = useState(); - const isAssistedTaggingAccessible = !!user - ?.accessibleFeatures?.some((feature) => feature.key === 'ASSISTED'); - const [ isNavigationDisabled, setNavigationDisableState, @@ -308,10 +179,6 @@ function AnalyticalFramework(props: Props) { secondaryTaggingPristine, setSecondaryTaggingPristine, ] = useState(true); - const [ - predictionTagsMappingPristine, - setPredictionTagsMappingPristine, - ] = useState(true); const [ frameworkImage, @@ -351,7 +218,6 @@ function AnalyticalFramework(props: Props) { setOrganizationOptions(storedAfArg.organizationOptions); setPrimaryTaggingPristine(storedAfArg.primaryTaggingPristine); setSecondaryTaggingPristine(storedAfArg.secondaryTaggingPristine); - setPredictionTagsMappingPristine(storedAfArg.predictionTagsMappingPristine); setCacheUsed(true); } @@ -399,7 +265,6 @@ function AnalyticalFramework(props: Props) { setPristine(true); setPrimaryTaggingPristine(true); setSecondaryTaggingPristine(true); - setPredictionTagsMappingPristine(true); if (storedData) { // NOTE: clearing out stored af after new data is fetched @@ -447,7 +312,6 @@ function AnalyticalFramework(props: Props) { setPristine(true); setPrimaryTaggingPristine(true); setSecondaryTaggingPristine(true); - setPredictionTagsMappingPristine(true); const path = generatePath( routes.analyticalFrameworkEdit.path, @@ -502,7 +366,6 @@ function AnalyticalFramework(props: Props) { setValue(transformFramework(result as Framework)); setPrimaryTaggingPristine(true); setSecondaryTaggingPristine(true); - setPredictionTagsMappingPristine(true); } }, onError: (error) => { @@ -613,7 +476,6 @@ function AnalyticalFramework(props: Props) { pristine, primaryTaggingPristine, secondaryTaggingPristine, - predictionTagsMappingPristine, framework: value, frameworkImage, organizationOptions, @@ -627,7 +489,6 @@ function AnalyticalFramework(props: Props) { value, primaryTaggingPristine, secondaryTaggingPristine, - predictionTagsMappingPristine, frameworkImage, organizationOptions, ], @@ -636,66 +497,15 @@ function AnalyticalFramework(props: Props) { const handlePrimaryTaggingChange = useCallback( (val: SetValueArg) => { setPrimaryTaggingPristine(false); - setPredictionTagsMappingPristine(false); - setValue((oldVal) => { - const newData = (typeof val === 'function') ? val(oldVal.primaryTagging) : val; - const primaryTaggingWidgets = newData?.map( - (item) => item.widgets, - ).flat().filter(isDefined) ?? []; - const newMappings = stripGhostAssociations( - [ - ...(oldVal.secondaryTagging ?? []), - ...primaryTaggingWidgets, - ], - oldVal.predictionTagsMapping, - ); - return { - ...oldVal, - primaryTagging: newData, - predictionTagsMapping: newMappings, - }; - }, true); + setFieldValue(val, 'primaryTagging'); }, - [setValue], + [setFieldValue], ); const handleSecondaryTaggingChange = useCallback( (val: SetValueArg) => { setSecondaryTaggingPristine(false); - setPredictionTagsMappingPristine(false); - setValue((oldVal) => { - const newData = (typeof val === 'function') ? val(oldVal.secondaryTagging) : val; - const primaryTaggingWidgets = oldVal.primaryTagging?.map( - (item) => item.widgets, - ).flat().filter(isDefined) ?? []; - const newMappings = stripGhostAssociations( - [ - ...primaryTaggingWidgets, - ...(newData ?? []), - ], - oldVal.predictionTagsMapping, - ); - return { - ...oldVal, - secondaryTagging: newData, - predictionTagsMapping: newMappings, - }; - }, true); - }, - [setValue], - ); - - const handlePredictionTagsMappings = useCallback( - (val: SetValueArg, name: 'predictionTagsMapping') => { - setPredictionTagsMappingPristine(false); - setFieldValue(val, name); - }, - [setFieldValue], - ); - - const handleAssistedTaggingStatusChange = useCallback( - (val: boolean) => { - setFieldValue(val, 'assistedTaggingEnabled'); + setFieldValue(val, 'secondaryTagging'); }, [setFieldValue], ); @@ -744,12 +554,6 @@ function AnalyticalFramework(props: Props) { parentWidgetType: undefined, } : null, })), - predictionTagsMapping: predictionTagsMappingPristine - ? undefined - : val.predictionTagsMapping?.map((item) => ({ - ...item, - widgetType: undefined, - })), }; const data = newData as AnalysisFrameworkInputType; @@ -781,19 +585,11 @@ function AnalyticalFramework(props: Props) { [ setError, validate, frameworkId, primaryTaggingPristine, secondaryTaggingPristine, - predictionTagsMappingPristine, updateAnalysisFramework, createAnalysisFramework, ], ); - const { - data: assistedTagsResponse, - loading: assistedTagsPending, - } = useQuery( - ASSISTED_PREDICTION_TAGS_QUERY, - ); - - const pending = creatingAnalysisFramework || updatingAnalysisFramework || assistedTagsPending; + const pending = creatingAnalysisFramework || updatingAnalysisFramework; const error = getErrorObject(riskyError); @@ -801,7 +597,6 @@ function AnalyticalFramework(props: Props) { detailsErrored, primaryTaggingErrored, secondaryTaggingErrored, - predictionTagsMappingErrored, propertiesErrored, ] = useMemo( () => { @@ -809,13 +604,11 @@ function AnalyticalFramework(props: Props) { delete errorWithoutTaggings.primaryTagging; delete errorWithoutTaggings.secondaryTagging; delete errorWithoutTaggings.properties; - delete errorWithoutTaggings.predictionTagsMapping; return [ analyzeErrors(errorWithoutTaggings), analyzeErrors(error?.primaryTagging), analyzeErrors(error?.secondaryTagging), - analyzeErrors(error?.predictionTagsMapping), analyzeErrors(error?.properties), ]; }, @@ -948,18 +741,6 @@ function AnalyticalFramework(props: Props) { > {_ts('analyticalFramework', 'review')} - {isAssistedTaggingAccessible && ( - - Assisted Tagging - - )} {value.isVisualizationEnabled && ( - {isAssistedTaggingAccessible && ( - - - - )} {value.isVisualizationEnabled && ( ; +export type PartialFormType = PartialForm; export type WidgetsType = NonNullable; export type SectionsType = NonNullable; export type PropertiesType = NonNullable; export type StatsConfigType = NonNullable['statsConfig']>; -export type PredictionTagMappingsType = NonNullable; export type PartialWidgetsType = WidgetsType; export type PartialSectionsType = SectionsType; -export type PartialPredictionTagMappingsType = PredictionTagMappingsType; // NOTE: These are not partials widget types even if it's on the name type PartialWidgetType = WidgetsType[number]; type PartialSectionType = SectionsType[number]; -type PartialPredictionTagMappingType = PredictionTagMappingsType[number]; type FormSchema = ObjectSchema; type FormSchemaFields = ReturnType; -type PredictionTagMappingSchema = ObjectSchema; -type PredictionTagMappingSchemaFields = ReturnType; -const predictionTagMappingSchema: PredictionTagMappingSchema = { - fields: (): PredictionTagMappingSchemaFields => ({ - association: [], - tag: [], - - widget: [], - widgetType: [], - clientId: [], - id: [defaultUndefinedType], - }), -}; - -type PredictionTagMappingsSchema = ArraySchema; -type PredictionTagMappingsSchemaMember = ReturnType; -const predictionTagMappingsSchema: PredictionTagMappingsSchema = { - keySelector: (col) => col.clientId, - member: (): PredictionTagMappingsSchemaMember => predictionTagMappingSchema, -}; - type WidgetSchema = ObjectSchema; type WidgetSchemaFields = ReturnType; const widgetSchema: WidgetSchema = { @@ -132,8 +108,6 @@ const schema: FormSchema = { primaryTagging: sectionsSchema, secondaryTagging: widgetsSchema, - - predictionTagsMapping: predictionTagMappingsSchema, }; if (value?.isVisualizationEnabled) { diff --git a/app/views/AnalyticalFramework/types.tsx b/app/views/AnalyticalFramework/types.tsx index 5eeb03f53b..e5daaafca4 100644 --- a/app/views/AnalyticalFramework/types.tsx +++ b/app/views/AnalyticalFramework/types.tsx @@ -4,15 +4,12 @@ import { import { WidgetType as WidgetRaw, - AnalysisFrameworkPredictionMappingType as MappingsItemRaw, WidgetGqlInputType as WidgetInputRaw, - PredictionTagAnalysisFrameworkMapInputType as MappingsItemInputRaw, CurrentFrameworkQuery, AnalysisFrameworkInputType, } from '#generated/types'; import { Widget as WidgetFromAF, - MappingsItem, } from '#types/newAnalyticalFramework'; import { DeepMandatory, @@ -23,15 +20,11 @@ import { // Remove this DeepMandatory transformation after server sends key as mandatory export type FrameworkRaw = DeepMandatory, 'key'>; type FrameworkWithWidgets = DeepReplace, WidgetFromAF>; -type FrameworkWithTags = DeepReplace; -export type Framework = FrameworkWithTags; +export type Framework = FrameworkWithWidgets; export type FrameworkInputRaw = DeepMandatory, 'clientId' | 'key' | 'widgetId' | 'order' | 'conditional'>; type FrameworkInputWithWidgets = DeepReplace; -type FrameworkInputWithTags = DeepReplace< - FrameworkInputWithWidgets, MappingsItemInputRaw, MappingsItem ->; -export type FrameworkInput = Omit & { +export type FrameworkInput = Omit & { previewImage?: File | null, }; export type Section = NonNullable[number]>;