Skip to content

Commit 59b0e8c

Browse files
New cells for the last messages (#61)
* New cells for the last messages * Dropdown with checkboxes update * Cell visual changes * Cell statuses and dropdown with text truncation * Cell rename * Cell component change * Strategy steps improvements * PR comments * PR comments * BUild fix * PR comments --------- Co-authored-by: JWittmeyer <jens.wittmeyer@kern.ai>
1 parent 6dacb81 commit 59b0e8c

File tree

5 files changed

+75
-16
lines changed

5 files changed

+75
-16
lines changed

components/KernDropdown.tsx

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export default function KernDropdown(props: KernDropdownProps) {
6161
setDropdownCaptions(prepareOptions);
6262
setSearchIndexes(null);
6363
}
64-
}, [props.options, searchText, selectedCheckboxes, props.hasSearchBar, props.hasCheckboxes, props.selectedCheckboxes, props.hasSelectAll, props.valuePropertyPath]);
64+
}, [props.options, searchText, props.hasSearchBar, props.hasCheckboxes, props.valuePropertyPath, props.selectedCheckboxes, props.hasSelectAll]);
6565

6666
useEffect(() => {
6767
if (!props.disabledOptions || !props.options) return;
@@ -94,8 +94,7 @@ export default function KernDropdown(props: KernDropdownProps) {
9494
return { "maxHeight": `${maxHeight}rem`, "overflowY": "auto" };
9595
}, [props.scrollAfterNOptions]);
9696

97-
function setOptionsWithCheckboxes(options: any[]) {
98-
if (selectedCheckboxes.length > 0) return;
97+
const setOptionsWithCheckboxes = useCallback((options: any[]) => {
9998
const newSelectedCheckboxes = options.map((option: any, index: number) => {
10099
return {
101100
name: option,
@@ -105,25 +104,27 @@ export default function KernDropdown(props: KernDropdownProps) {
105104
if (props.hasSelectAll) {
106105
newSelectedCheckboxes.push({
107106
name: SELECT_ALL,
108-
checked: false
107+
checked: newSelectedCheckboxes.every((checkbox) => checkbox.checked)
109108
});
110109
}
111110
setSelectedCheckboxes(newSelectedCheckboxes);
112111
setDropdownCaptions(newSelectedCheckboxes.map((option: any) => option.name));
113-
}
112+
}, [props.hasSelectAll, props.selectedCheckboxes]);
114113

115114
function toggleDropdown() {
116115
if (isDisabled && !props.hasCheckboxes) return; // if the dropdown has checkboxes, it shouldn't be disabled because the user can still select options
117116
if (isOpen && props.keepDrownOpen) return;
118117
setIsOpen(!isOpen);
119118
}
120119

121-
function handleSelectedCheckboxes(option: string, index: number, e: any) {
120+
const handleSelectedCheckboxes = useCallback((option: string, index: number, e: any) => {
122121
let newSelectedCheckboxes = [...selectedCheckboxes];
123122
if (option == SELECT_ALL) {
124123
newSelectedCheckboxes.forEach((checkbox) => {
125124
checkbox.checked = e.target.checked;
126125
});
126+
const allSelected = newSelectedCheckboxes.every((checkbox) => checkbox.checked);
127+
newSelectedCheckboxes[newSelectedCheckboxes.length - 1].checked = allSelected;
127128
} else {
128129
const lastIdx = newSelectedCheckboxes.length - 1;
129130
if (props.hasSelectAll && newSelectedCheckboxes[lastIdx].checked) {
@@ -136,7 +137,7 @@ export default function KernDropdown(props: KernDropdownProps) {
136137
newSelectedCheckboxes = newSelectedCheckboxes.filter((checkbox) => checkbox.name != SELECT_ALL);
137138
}
138139
props.selectedOption(newSelectedCheckboxes);
139-
}
140+
}, [selectedCheckboxes, props.hasSelectAll]);
140141

141142
function handleSelectedCheckboxesThreeStates(index: number) {
142143
const optionSave = { ...props.options[index] };
@@ -161,8 +162,7 @@ export default function KernDropdown(props: KernDropdownProps) {
161162
setSavedIndex(index);
162163
}
163164

164-
165-
function performActionOnClick(option: string, index: number) {
165+
const performActionOnClick = useCallback((option: string, index: number) => {
166166
if (props.hasCheckboxes) {
167167
handleSelectedCheckboxes(option, index, { target: { checked: !selectedCheckboxes[index].checked } });
168168
return;
@@ -180,8 +180,9 @@ export default function KernDropdown(props: KernDropdownProps) {
180180
props.selectedOption(props.options[index]);
181181
}
182182
setIsOpen(false);
183+
setSelectedCheckboxes([]);
183184
}
184-
}
185+
}, [props, selectedCheckboxes, searchIndexes]);
185186

186187
return (
187188
<Menu ref={dropdownRef} as="div" className={`relative inline-block text-left ${props.dropdownWidth ?? 'w-full'} ${props.dropdownClasses ?? ''} ${props.fontClass ?? ''}`}>
@@ -213,8 +214,8 @@ export default function KernDropdown(props: KernDropdownProps) {
213214
</Menu.Button>
214215
) : (<Menu.Button onClick={toggleDropdown} className={`inline-flex w-full justify-between items-center rounded-md border border-gray-300 px-4 py-2 text-sm font-semibold text-gray-700 shadow-sm focus:outline-none focus:ring-2
215216
focus:ring-gray-300 focus:ring-offset-2 focus:ring-offset-gray-100 disabled:opacity-50 disabled:cursor-not-allowed ${props.buttonClasses ?? ''} ${props.buttonCaptionBgColor ?? 'bg-white hover:bg-gray-50'}`}
216-
disabled={isDisabled && !props.hasCheckboxes}>
217-
<div className='flex items-center gap-x-1'>
217+
disabled={isDisabled}>
218+
<div className={`flex items-center gap-x-1 ${props.truncateButtonName ? 'max-w-[300px] truncate' : ''}`}>
218219
{props.buttonPrefixIcon}
219220
{!props.hasCheckboxesThreeStates && props.buttonName}
220221
</div>
@@ -267,7 +268,7 @@ export default function KernDropdown(props: KernDropdownProps) {
267268
if (!props.optionsHaveHoverBox) return;
268269
setHoverBoxPosition(null);
269270
}}>
270-
{props.hasCheckboxes && <input checked={selectedCheckboxes[index].checked} name="option" type="checkbox" className="mr-3 cursor-pointer"
271+
{props.hasCheckboxes && <input checked={!!selectedCheckboxes[index]?.checked} name="option" type="checkbox" className="mr-3 cursor-pointer"
271272
onChange={(e) => handleSelectedCheckboxes(option, index, e)} />}
272273
{props.hasCheckboxesThreeStates && <div className="h-4 w-4 border-gray-300 mr-3 border rounded hover:bg-gray-200 min-w-4"
273274
style={{ backgroundColor: getActiveNegateGroupColor(props.options[index]), borderColor: getActiveNegateGroupColor(props.options[index]) }}>

components/kern-icons/icons.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,4 +184,4 @@ export const MemoIconArrowNarrowRight = memo(IconArrowNarrowRight);
184184
export const MemoIconMinus = memo(IconMinus);
185185
export const MemoIconDragDrop2 = memo(IconDragDrop2);
186186
export const MemoIconCircleDotted = memo(IconCircleDotted);
187-
export const MemoIconWorld = memo(IconWorld);
187+
export const MemoIconWorld = memo(IconWorld);

components/kern-table/CellComponents.tsx

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,4 +345,54 @@ function ConfigReleaseNotificationCell({ onClickView, onClickEdit }) {
345345
</div>;
346346
}
347347

348-
export { OrganizationAndUsersCell, MaxRowsColsCharsCell, CommentsCell, ExportConsumptionAndDeleteCell, BadgeCell, OrganizationUserCell, DeleteCell, LevelCell, ArchiveReasonCell, ProjectNameTaskCell, CancelTaskCell, IconCell, ConfigCell, EditDeleteOrgButtonCell, ViewStackCell, AbortSessionButtonCell, FeedbackMessageCell, FeedbackMessageTextCell, JumpToConversationCell, RemoteVersionCell, ExternalLinkCell, ModelDateCell, FileSizeCell, StatusModelCell, DeleteModelCell, LabelCell, ViewCell, EvaluationRunStateCell, EvaluationRunDetailsCell, EtlApiTokenCell, EmailCell, EditIntegrationCell, ExpiredTokenCell, LinkCell, ConfigReleaseNotificationCell }
348+
function TruncateAndTooltipCell({ value, hasError = false }) {
349+
return <div className="flex items-center">
350+
{hasError && <MemoIconAlertTriangleFilled className="h-5 w-5 text-red-600 mr-2" />}
351+
{value ? <Tooltip content={<span className="block max-w-[300px] break-words max-h-[500px] overflow-y-auto">{value}</span>} color="invert" hideArrow={true} placement='bottom'>
352+
<span className="block max-w-56 truncate">{value}</span>
353+
</Tooltip> : <NotApplicableBadge />}
354+
</div>;
355+
}
356+
357+
function JumpToConversationAndAssignCell({ onClick, jumpTo }) {
358+
return <div className="flex justify-center">
359+
<Tooltip content={`Assign user to the org and jump to ${jumpTo}`} color="invert" className="cursor-auto">
360+
<button onClick={onClick}
361+
className='inline-flex p-2 items-center justify-center rounded-lg hover:bg-gray-200'>
362+
<MemoIconArrowRight className='h-4 w-4' />
363+
</button>
364+
</Tooltip>
365+
</div>
366+
}
367+
368+
function TaskStateCell({ value, color, tooltipValue }) {
369+
const className = useMemo(() => {
370+
return `inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${color === 'green'
371+
? 'bg-green-300'
372+
: 'bg-' + color + '-100 text-' + color + '-800'}`;
373+
}, [color]);
374+
375+
return (
376+
<>
377+
{tooltipValue ? (
378+
<Tooltip content={tooltipValue} color="invert" className="cursor-auto">
379+
<span className={className}>
380+
<svg className={`mr-1.5 h-2 w-2 ${'text-' + color + '-400'}`} fill="currentColor" viewBox="0 0 8 8">
381+
<circle cx="4" cy="4" r="3" />
382+
</svg>
383+
<span className={' text-' + color + '-800'}>{value}</span>
384+
</span>
385+
</Tooltip>
386+
) : (
387+
<span className={className}>
388+
<svg className={`mr-1.5 h-2 w-2 ${'text-' + color + '-400'}`} fill="currentColor" viewBox="0 0 8 8">
389+
<circle cx="4" cy="4" r="3" />
390+
</svg>
391+
<span className={' text-' + color + '-800'}>{value}</span>
392+
</span>
393+
)}
394+
</>
395+
);
396+
}
397+
398+
export { OrganizationAndUsersCell, MaxRowsColsCharsCell, CommentsCell, ExportConsumptionAndDeleteCell, BadgeCell, OrganizationUserCell, DeleteCell, LevelCell, ArchiveReasonCell, ProjectNameTaskCell, CancelTaskCell, IconCell, ConfigCell, EditDeleteOrgButtonCell, ViewStackCell, AbortSessionButtonCell, FeedbackMessageCell, FeedbackMessageTextCell, JumpToConversationCell, RemoteVersionCell, ExternalLinkCell, ModelDateCell, FileSizeCell, StatusModelCell, DeleteModelCell, LabelCell, ViewCell, EvaluationRunStateCell, EvaluationRunDetailsCell, EtlApiTokenCell, EmailCell, EditIntegrationCell, ExpiredTokenCell, LinkCell, ConfigReleaseNotificationCell, TruncateAndTooltipCell, JumpToConversationAndAssignCell, TaskStateCell }

components/kern-table/KernTable.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import SortArrows from "@/submodules/react-components/components/kern-table/SortArrows";
22
import { KernTableProps } from "../../types/kern-table";
3-
import { AbortSessionButtonCell, ArchiveReasonCell, BadgeCell, CancelTaskCell, CommentsCell, ConfigCell, DeleteModelCell, DeleteCell, EditDeleteOrgButtonCell, EmailCell, EtlApiTokenCell, EvaluationRunDetailsCell, EvaluationRunStateCell, ExportConsumptionAndDeleteCell, ExternalLinkCell, FeedbackMessageCell, FeedbackMessageTextCell, FileSizeCell, IconCell, JumpToConversationCell, LabelCell, LevelCell, MaxRowsColsCharsCell, ModelDateCell, OrganizationAndUsersCell, OrganizationUserCell, ProjectNameTaskCell, RemoteVersionCell, StatusModelCell, ViewCell, ViewStackCell, EditIntegrationCell, ExpiredTokenCell, LinkCell, ConfigReleaseNotificationCell } from "./CellComponents";
3+
import { AbortSessionButtonCell, ArchiveReasonCell, BadgeCell, CancelTaskCell, CommentsCell, ConfigCell, DeleteModelCell, DeleteCell, EditDeleteOrgButtonCell, EmailCell, EtlApiTokenCell, EvaluationRunDetailsCell, EvaluationRunStateCell, ExportConsumptionAndDeleteCell, ExternalLinkCell, FeedbackMessageCell, FeedbackMessageTextCell, FileSizeCell, IconCell, JumpToConversationCell, LabelCell, LevelCell, MaxRowsColsCharsCell, ModelDateCell, OrganizationAndUsersCell, OrganizationUserCell, ProjectNameTaskCell, RemoteVersionCell, StatusModelCell, ViewCell, ViewStackCell, EditIntegrationCell, ExpiredTokenCell, LinkCell, ConfigReleaseNotificationCell, TruncateAndTooltipCell, JumpToConversationAndAssignCell, TaskStateCell } from "./CellComponents";
44
import { Fragment, useMemo } from "react";
55
import KernDropdown from "../KernDropdown";
66
import { NotApplicableBadge } from "@/submodules/react-components/components/Badges";
@@ -162,6 +162,12 @@ function ComponentMapper(cell: any) {
162162
return <LinkCell {...cell} />;
163163
case 'ConfigReleaseNotificationCell':
164164
return <ConfigReleaseNotificationCell {...cell} />;
165+
case 'TruncateAndTooltipCell':
166+
return <TruncateAndTooltipCell {...cell} />;
167+
case 'JumpToConversationAndAssignCell':
168+
return <JumpToConversationAndAssignCell {...cell} />;
169+
case 'TaskStateCell':
170+
return <TaskStateCell {...cell} />;
165171
case '@provided@':
166172
return cell.jsx ?? <NotApplicableBadge />;
167173
}

types/dropdown.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import React from "react";
4444
* @positionDropdown {string} - The position of the dropdown
4545
* @dropdownAdd {JSX.Element} - array of JSX elements that will be added to the dropdown items
4646
* @forceOverwriteOpen {boolean} - forces the dropdown to stay open until set to false/undefined
47+
* @truncateButtonName {boolean} - If the button name should be truncated when it exceeds the button width
4748
*/
4849
export type KernDropdownProps = {
4950
buttonName?: string;
@@ -93,6 +94,7 @@ export type KernDropdownProps = {
9394
scrollAfterNOptions?: number;
9495
dropdownAdd?: JSX.Element[];
9596
forceOverwriteOpen?: boolean;
97+
truncateButtonName?: boolean;
9698
placeholder?: string;
9799
}
98100

0 commit comments

Comments
 (0)