Skip to content

Commit 5ab9d5d

Browse files
Merge pull request #78 from code-kern-ai/ac-tags-extension
Add text list attribute for sharepoint integration tags
2 parents e534e85 + 019eedd commit 5ab9d5d

File tree

27 files changed

+163
-70
lines changed

27 files changed

+163
-70
lines changed

src/components/projects/projectId/attributes/attributeId/ExecutionContainer.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export default function ExecutionContainer(props: ExecutionContainerProps) {
4545
setRequestedSomething(false);
4646
props.setEnabledButton(false);
4747
setRunOn10HasError(sampleRecordsFinal.calculatedAttributes.length > 0 ? false : true);
48-
if (currentAttributesRef.current.dataType == DataTypeEnum.EMBEDDING_LIST) {
48+
if (currentAttributesRef.current.dataType == DataTypeEnum.EMBEDDING_LIST || currentAttributesRef.current.dataType == DataTypeEnum.TEXT_LIST) {
4949
sampleRecordsFinal.calculatedAttributesList = sampleRecordsFinal.calculatedAttributes.map((record: string) => JSON.parse(record));
5050
sampleRecordsFinal.calculatedAttributesListDisplay = extendArrayElementsByUniqueId(sampleRecordsFinal.calculatedAttributesList);
5151
}
@@ -75,7 +75,13 @@ export default function ExecutionContainer(props: ExecutionContainerProps) {
7575
const sampleRecordsFinal = useMemo(() => {
7676
if (sampleRecords && sampleRecords.calculatedAttributesDisplay) {
7777
return sampleRecords.calculatedAttributesDisplay.map((record: any, index) => {
78-
const calculatedValue = currentAttributesRef.current.dataType != DataTypeEnum.EMBEDDING_LIST ? record : { id: record.id, value: JSON.parse(record.value) }
78+
let calculatedValue;
79+
if (currentAttributesRef.current.dataType == DataTypeEnum.EMBEDDING_LIST || currentAttributesRef.current.dataType == DataTypeEnum.TEXT_LIST) {
80+
calculatedValue = { id: record.id, value: JSON.parse(record.value) };
81+
}
82+
else {
83+
calculatedValue = record
84+
}
7985
return {
8086
...record,
8187
onClick: viewRecordDetails(index),
@@ -85,7 +91,6 @@ export default function ExecutionContainer(props: ExecutionContainerProps) {
8591
);
8692
}
8793
}, [sampleRecords]);
88-
8994
return (<div>
9095
<div className="mt-8 text-sm leading-5">
9196
<div className="text-gray-700 font-medium mr-2">
@@ -136,7 +141,7 @@ export default function ExecutionContainer(props: ExecutionContainerProps) {
136141
<div key={record.id} className="divide-y divide-gray-200 bg-white">
137142
<div className="flex-shrink-0 border-b border-gray-200 shadow-sm flex justify-between items-center">
138143
<div className="flex items-center text-xs leading-5 text-gray-500 font-normal mx-4 my-3 text-justify">
139-
{String(record.value)}
144+
{Array.isArray(record?.calculatedValue?.value) ? JSON.stringify(record?.calculatedValue?.value) : String(record?.calculatedValue?.value)}
140145
</div>
141146
<div className="flex items-center justify-center mr-5 ml-auto">
142147
<KernButton

src/components/projects/projectId/attributes/attributeId/LLM/LLMPlaygroundModal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ export default function LLMPlaygroundModal() {
156156
<div className="grid grid-cols-2 gap-2 text-sm max-h-52 overflow-y-auto" style={{ gridTemplateColumns: `max-content auto` }}>
157157
{recordKeys.map((rk) => <Fragment key={rk.name}>
158158
<label className="block font-bold text-gray-900">{rk.name}</label>
159-
{recordData.map((record) => rk.dataType == DataTypeEnum.EMBEDDING_LIST ? <div key={record.running_id} className="flex flex-col divide-y divide-gray-200">
159+
{recordData.map((record) => (rk.dataType == DataTypeEnum.EMBEDDING_LIST || rk.dataType == DataTypeEnum.TEXT_LIST) ? <div key={record.running_id} className="flex flex-col divide-y divide-gray-200">
160160
{record[rk.name].map((li, idx) => <span key={idx} className="text-gray-700">{li}</span>)}
161161
</div> : <span key={record.running_id} className="text-gray-700">{record[rk.name]}</span>)}
162162
</Fragment>)}

src/components/projects/projectId/attributes/attributeId/ViewRecordDetailsModal.tsx

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,21 @@ import { ModalEnum } from "@/src/types/shared/modal";
44
import { useSelector } from "react-redux";
55
import style from '@/src/styles/components/projects/projectId/attribute-calculation.module.css';
66
import { RecordDisplay } from "@/src/components/shared/record-display/RecordDisplay";
7-
import { DataTypeEnum } from "@/src/types/shared/general";
87
import { ViewRecordDetailsModalProps } from "@/src/types/components/projects/projectId/settings/attribute-calculation";
98
import { selectVisibleAttributesHeuristics } from "@/src/reduxStore/states/pages/settings";
9+
import { useMemo } from "react";
1010

1111
export default function ViewRecordDetailsModal(props: ViewRecordDetailsModalProps) {
1212
const modalViewRecordDetails = useSelector(selectModal(ModalEnum.VIEW_RECORD_DETAILS));
1313
const attributes = useSelector(selectVisibleAttributesHeuristics);
1414

15+
const displayValue = useMemo(() => {
16+
if (!props.sampleRecords || !modalViewRecordDetails.open) return null;
17+
return Array.isArray(props.sampleRecords[modalViewRecordDetails.recordIdx].calculatedValue.value)
18+
? JSON.stringify(props.sampleRecords[modalViewRecordDetails.recordIdx].calculatedValue.value)
19+
: String(props.sampleRecords[modalViewRecordDetails.recordIdx].calculatedValue.value);
20+
}, [props.sampleRecords, modalViewRecordDetails.recordIdx, modalViewRecordDetails.open]);
21+
1522
return (<>
1623
{modalViewRecordDetails.open && modalViewRecordDetails.record && props.sampleRecords && <>
1724
<Modal modalName={ModalEnum.VIEW_RECORD_DETAILS} className="md:max-w-5xl">
@@ -23,13 +30,9 @@ export default function ViewRecordDetailsModal(props: ViewRecordDetailsModalProp
2330
record={modalViewRecordDetails.record} />
2431
<div className="text-sm leading-5 text-left text-gray-900 font-medium">Calculated value</div>
2532
<div className="text-sm leading-5 text-left text-gray-500 font-normal whitespace-pre-line">
26-
{props.currentAttribute.dataType != DataTypeEnum.EMBEDDING_LIST ? <span>
27-
{props.sampleRecords[modalViewRecordDetails.recordIdx].calculatedValue.value}
28-
</span> : <div className="flex flex-col gap-y-2 divide-y">
29-
{props.sampleRecords[modalViewRecordDetails.recordIdx].calculatedValue.value.map((item: any) => <span key={item} className="mt-1">
30-
{item}
31-
</span>)}
32-
</div>}
33+
<div className="flex flex-col gap-y-2 divide-y">
34+
{displayValue}
35+
</div>
3336
</div>
3437
</div>
3538
</Modal>

src/components/projects/projectId/data-browser/DataBrowser.tsx

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,17 +40,24 @@ export default function DataBrowser() {
4040
const activeSlice = useSelector(selectActiveSlice);
4141

4242
const [searchRequest, setSearchRequest] = useState(SEARCH_REQUEST);
43+
const [clearRequest, setClearRequest] = useState(false);
4344

4445
useEffect(() => {
45-
if (!projectId) return;
46+
if (!projectId || !attributes) return;
4647
if (!users || !user) return;
4748
refetchDataSlicesAndProcess();
4849
refetchAttributesAndProcess();
4950
refetchLabelingTasksAndProcess();
5051
refetchEmbeddingsAndPostProcess();
51-
refetchUniqueValuesAndProcess();
5252
}, [projectId, users, user]);
5353

54+
useEffect(() => {
55+
if (!projectId || !attributes) return;
56+
getUniqueValuesByAttributes(projectId, (res) => {
57+
dispatch(setUniqueValuesDict(postProcessUniqueValues(res, attributes)));
58+
});
59+
}, [projectId, attributes]);
60+
5461
useEffect(() => {
5562
if (!projectId || !labelingTasks || !recordList) return;
5663
refetchRecordCommentsAndProcess(recordList);
@@ -140,14 +147,9 @@ export default function DataBrowser() {
140147
setSearchRequest({ offset: searchRequest.offset + searchRequest.limit, limit: searchRequest.limit });
141148
}
142149

143-
function refetchUniqueValuesAndProcess() {
144-
getUniqueValuesByAttributes(projectId, (res) => {
145-
dispatch(setUniqueValuesDict(postProcessUniqueValues(res, attributes)));
146-
});
147-
}
148-
149150
const setSearchRequestToInit = useCallback(() => {
150151
setSearchRequest(SEARCH_REQUEST);
152+
setClearRequest(true);
151153
}, []);
152154

153155
const handleWebsocketNotification = useCallback((msgParts: string[]) => {
@@ -171,7 +173,7 @@ export default function DataBrowser() {
171173

172174
return (<>
173175
{projectId && <div className="flex flex-row h-full">
174-
<DataBrowserSidebar />
176+
<DataBrowserSidebar clearRequest={clearRequest} />
175177
<DataBrowserRecords refetchNextRecords={getNextRecords} clearSearchRequest={setSearchRequestToInit} />
176178
</div>}
177179
</>)

src/components/projects/projectId/data-browser/DataBrowserRecords.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { openModal } from '@/src/reduxStore/states/modal';
2-
import { selectActiveSearchParams, selectActiveSlice, selectAdditionalData, selectRecords, selectSimilaritySearch, setActiveDataSlice, setActiveSearchParams, setIsTextHighlightNeeded, setRecordsInDisplay, setTextHighlight, updateAdditionalDataState } from '@/src/reduxStore/states/pages/data-browser';
2+
import { selectActiveSearchParams, selectActiveSlice, selectAdditionalData, selectRecords, selectSimilaritySearch, setActiveDataSlice, setActiveSearchParams, setFullSearchStore, setIsTextHighlightNeeded, setRecordsInDisplay, setTextHighlight, updateAdditionalDataState } from '@/src/reduxStore/states/pages/data-browser';
33
import style from '@/src/styles/components/projects/projectId/data-browser.module.css';
44
import { ModalEnum } from '@/src/types/shared/modal';
55
import { TOOLTIPS_DICT } from '@/src/util/tooltip-constants';

src/components/projects/projectId/data-browser/DataBrowserSidebar.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { selectAllUsers } from '@/src/reduxStore/states/general';
22
import { setModalStates } from '@/src/reduxStore/states/modal';
3-
import { selectActiveSlice, selectAdditionalData, selectDataSlicesAll, setActiveDataSlice, setActiveSearchParams, setFullSearchStore, setIsTextHighlightNeeded, setRecordsInDisplay, setTextHighlight, updateAdditionalDataState } from '@/src/reduxStore/states/pages/data-browser';
3+
import { selectActiveSlice, selectAdditionalData, selectDataSlicesAll, setActiveDataSlice, setActiveSearchParams, setFullSearchStore, setIsTextHighlightNeeded, setRecordsInDisplay, setTextHighlight, setUniqueValuesDict, updateAdditionalDataState } from '@/src/reduxStore/states/pages/data-browser';
44
import { selectProjectId } from '@/src/reduxStore/states/project';
55
import style from '@/src/styles/components/projects/projectId/data-browser.module.css';
6-
import { DataSlice } from '@/src/types/components/projects/projectId/data-browser/data-browser';
6+
import { DataBrowserSidebarProps, DataSlice } from '@/src/types/components/projects/projectId/data-browser/data-browser';
77
import { ModalEnum } from '@/src/types/shared/modal';
88
import { updateSliceInfoHelper } from '@/src/util/components/projects/projectId/data-browser/data-browser-helper';
99
import { Slice } from '@/submodules/javascript-functions/enums/enums';
@@ -17,7 +17,7 @@ import DataSliceInfoModal from './modals/DataSliceInfoModal';
1717
import MultilineTooltip from '@/src/components/shared/multilines-tooltip/MultilineTooltip';
1818
import { MemoIconAlertTriangle, MemoIconInfoCircle, MemoIconLayoutSidebar, MemoIconTrash } from '@/submodules/react-components/components/kern-icons/icons';
1919

20-
export default function DataBrowserSidebar() {
20+
export default function DataBrowserSidebar(props: DataBrowserSidebarProps) {
2121
const dispatch = useDispatch();
2222

2323
const projectId = useSelector(selectProjectId);
@@ -130,7 +130,7 @@ export default function DataBrowserSidebar() {
130130
</div>
131131
<span className="text-sm text-gray-400">You can filter and order all your data in the browser according to your needs. Selected filter criteria can be saved and used later on.</span>
132132

133-
<SearchGroups />
133+
<SearchGroups clearRequest={props.clearRequest} />
134134
</div>}
135135
</div>
136136
<DeleteSliceModal />

src/components/projects/projectId/data-browser/RecordList.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ import SimilaritySearchModal from "./modals/SimilaritySeachModal";
1515
import RecordCommentsModal from "./modals/RecordCommentsModal";
1616
import ButtonAsText from "@/submodules/react-components/components/kern-button/ButtonAsText";
1717
import { MemoIconAngle, MemoIconArrowRight, MemoIconEdit, MemoIconNotes } from "@/submodules/react-components/components/kern-icons/icons";
18+
import { useEffect, useMemo } from "react";
19+
import { selectAllProjects, selectProject, setAllProjects } from "@/src/reduxStore/states/project";
20+
import { getAllProjects } from "@/src/services/base/project";
1821

1922
export default function RecordList(props: RecordListProps) {
2023
const dispatch = useDispatch();
@@ -25,6 +28,16 @@ export default function RecordList(props: RecordListProps) {
2528
const embeddings = useSelector(selectEmbeddings);
2629
const recordComments = useSelector(selectRecordComments);
2730
const attributes = useSelector(selectVisibleAttributesDataBrowser);
31+
const project = useSelector(selectProject);
32+
const allProjects = useSelector(selectAllProjects);
33+
34+
useEffect(() => {
35+
getAllProjects((projects) => dispatch(setAllProjects(projects)));
36+
}, []);
37+
38+
const isIntegrationProject = useMemo(() => {
39+
return allProjects && allProjects.find(p => p.id === project.id)?.isIntegrationProject;
40+
}, [allProjects, project.id]);
2841

2942
return (<>
3043
{recordList && recordList.map((record, index) => (<div key={record.id} className="bg-white overflow-hidden shadow rounded-lg border mb-4 pb-4 relative">
@@ -71,7 +84,7 @@ export default function RecordList(props: RecordListProps) {
7184
</div>
7285
</div>
7386
</div>
74-
{user?.role == UserRole.ENGINEER && <div className="p-2 cursor-pointer absolute right-2 top-2">
87+
{(user?.role == UserRole.ENGINEER && !isIntegrationProject) && <div className="p-2 cursor-pointer absolute right-2 top-2">
7588
<Tooltip content={TOOLTIPS_DICT.DATA_BROWSER.EDIT_RECORD} color="invert">
7689
<MemoIconEdit className="h-4 w-4" onClick={() => props.editRecord(index)} />
7790
</Tooltip></div>}

src/components/projects/projectId/data-browser/SearchGroups.tsx

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,12 @@ import { getRecordsByStaticSlice, searchRecordsExtended } from "@/src/services/b
2929
import { staticDataSlicesCurrentCount } from "@/src/services/base/dataSlices";
3030
import KernButton from "@/submodules/react-components/components/kern-button/KernButton";
3131
import { MemoIconArrowDown, MemoIconArrowsRandom, MemoIconFilterOff, MemoIconPlus, MemoIconPointerOff, MemoIconTrash } from "@/submodules/react-components/components/kern-icons/icons";
32+
import { SearchGroupsProps } from "@/src/types/components/projects/projectId/data-browser/data-browser";
3233

3334
const GROUP_SORT_ORDER = 0;
3435
let GLOBAL_SEARCH_GROUP_COUNT = 0;
3536

36-
export default function SearchGroups() {
37+
export default function SearchGroups(props: SearchGroupsProps) {
3738
const dispatch = useDispatch();
3839

3940
const projectId = useSelector(selectProjectId);
@@ -96,7 +97,7 @@ export default function SearchGroups() {
9697
});
9798
setBackgroundColors(colors);
9899
setAttributeSortOrder(attributesSort);
99-
}, [attributes]);
100+
}, [attributes, props.clearRequest]);
100101

101102
useEffect(() => {
102103
if (!attributesSortOrder || !searchGroupsStore) return;
@@ -336,7 +337,14 @@ export default function SearchGroups() {
336337
const attributeName = formControlsIdx['name'];
337338
attributeType = getAttributeType(attributesSortOrder, attributeName);
338339
}
339-
if (attributeType !== DataTypeEnum.BOOLEAN) {
340+
if (attributeType == DataTypeEnum.TEXT_LIST) {
341+
operatorsCopy.push({
342+
value: SearchOperator.CONTAINS.split("_").join(" "),
343+
});
344+
tooltipsCopy.push(getSearchOperatorTooltip(SearchOperator.CONTAINS));
345+
formControlsIdx['operator'] = SearchOperator.CONTAINS;
346+
347+
} else if (attributeType !== DataTypeEnum.BOOLEAN) {
340348
for (let t of Object.values(SearchOperator)) {
341349
operatorsCopy.push({
342350
value: t.split("_").join(" "),
@@ -612,17 +620,17 @@ export default function SearchGroups() {
612620
<div className="flex-grow mr-2.5 flex flex-col mt-2 ">
613621
<div className="flex-grow flex flex-row flex-wrap gap-1">
614622
<div style={{ width: groupItem.operator != '' ? '49%' : '100%' }}>
615-
<KernDropdown options={attributesSortOrder} buttonName={groupItem.name} backgroundColors={backgroundColors}
616-
selectedOption={(option: any) => selectValueDropdown(option.name, index, 'name', group.key)} fontClass="font-dmMono" buttonClasses="text-xs" />
623+
{attributesSortOrder && <KernDropdown options={attributesSortOrder} buttonName={groupItem.name} backgroundColors={backgroundColors}
624+
selectedOption={(option: any) => selectValueDropdown(option.name, index, 'name', group.key)} fontClass="font-dmMono" buttonClasses="text-xs" />}
617625
</div>
618626
<div style={{ width: '49%' }}>
619-
{groupItem.operator != '' &&
627+
{(groupItem.operator != '' && operatorsDropdown) &&
620628
<KernDropdown options={operatorsDropdown} buttonName={groupItem.operator} tooltipsArray={tooltipsArray} tooltipArrayPlacement="right" buttonClasses="text-xs"
621629
selectedOption={(option: any) => selectValueDropdown(option.value, index, 'operator', group.key)} fontClass="font-dmMono" />
622630
}
623631
</div>
624632
</div>
625-
{uniqueValuesDict[groupItem['name']] && groupItem['operator'] != '' && groupItem['operator'] != 'BETWEEN' && groupItem['operator'] != 'IN' && groupItem['operator'] != 'IN WC' ? (
633+
{(uniqueValuesDict && uniqueValuesDict[groupItem['name']] && groupItem['operator'] != '' && groupItem['operator'] != 'BETWEEN' && groupItem['operator'] != 'IN' && groupItem['operator'] != 'IN WC' || saveAttributeType == DataTypeEnum.TEXT_LIST) ? (
626634
<div className="my-2">
627635
<KernDropdown options={uniqueValuesDict[groupItem['name']]} buttonName={groupItem['searchValue'] ? groupItem['searchValue'] : 'Select value'}
628636
selectedOption={(option: any) => selectValueDropdown(option, index, 'searchValue', group.key)} fontClass="font-dmMono" />
@@ -641,7 +649,7 @@ export default function SearchGroups() {
641649
</div>
642650
)}
643651

644-
{(groupItem['operator'] == "BEGINS WITH" || groupItem['operator'] == "ENDS WITH" || groupItem['operator'] == SearchOperator.CONTAINS || groupItem['operator'] == "IN WC") && (saveAttributeType != DataTypeEnum.INTEGER && saveAttributeType != DataTypeEnum.FLOAT) &&
652+
{(groupItem['operator'] == "BEGINS WITH" || groupItem['operator'] == "ENDS WITH" || groupItem['operator'] == SearchOperator.CONTAINS || groupItem['operator'] == "IN WC") && (saveAttributeType != DataTypeEnum.INTEGER && saveAttributeType != DataTypeEnum.FLOAT && saveAttributeType != DataTypeEnum.TEXT_LIST) &&
645653
<label htmlFor="caseSensitive" className="text-xs text-gray-500 cursor-pointer flex items-center pb-2">
646654
<input name="caseSensitive" className="mr-1 cursor-pointer" id="caseSensitive"
647655
onChange={(e: any) => selectValueDropdown(e.target.checked, index, 'caseSensitive', group.key)} type="checkbox" />Case sensitive</label>}

0 commit comments

Comments
 (0)