status badge updates

This commit is contained in:
Nate Kelley 2025-03-03 16:30:38 -07:00
parent a5000067d6
commit 284e708862
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
8 changed files with 97 additions and 52 deletions

View File

@ -1,4 +1,4 @@
import { BASE_URL } from '@/api/buster_rest/instances'; import { BASE_URL } from '@/api/buster_rest/config';
import { PublicAssetResponse } from './interface'; import { PublicAssetResponse } from './interface';
import { FileType } from '@/api/asset_interfaces'; import { FileType } from '@/api/asset_interfaces';

View File

@ -40,7 +40,14 @@ const meta = {
onVerify: { onVerify: {
description: 'Function called when verification status is changed' description: 'Function called when verification status is changed'
} }
} },
decorators: [
(Story) => (
<div className="p-5">
<Story />
</div>
)
]
} satisfies Meta<typeof StatusBadgeButton>; } satisfies Meta<typeof StatusBadgeButton>;
export default meta; export default meta;

View File

@ -72,18 +72,18 @@ export const StatusBadgeButton: React.FC<{
const buttonText = Array.isArray(id) ? 'Status' : ''; const buttonText = Array.isArray(id) ? 'Status' : '';
return ( return (
<AppPopoverMenu // <AppPopoverMenu
items={items} // items={items}
trigger={['click']} // trigger={['click']}
onOpenChange={onOpenChange} // onOpenChange={onOpenChange}
open={isOpen} // open={isOpen}
hideCheckbox // hideCheckbox
doNotSortSelected={true} // doNotSortSelected={true}
disabled={disabled} // disabled={disabled}
destroyPopupOnHide={true} // destroyPopupOnHide={true}
selectedItems={selectedItem?.key ? [selectedItem.key! as string] : []} // selectedItems={selectedItem?.key ? [selectedItem.key! as string] : []}
placement="bottomRight" // placement="bottomRight"
headerContent={'Verification status...'}> // headerContent={'Verification status...'}>
<AppTooltip title={showButtonTooltip ? '' : 'Request verification from data team'}> <AppTooltip title={showButtonTooltip ? '' : 'Request verification from data team'}>
<Button <Button
disabled={disabled || ((!id || status === 'verified') && !isAdmin)} disabled={disabled || ((!id || status === 'verified') && !isAdmin)}
@ -92,7 +92,7 @@ export const StatusBadgeButton: React.FC<{
{buttonText} {buttonText}
</Button> </Button>
</AppTooltip> </AppTooltip>
</AppPopoverMenu> // </AppPopoverMenu>
); );
}); });
StatusBadgeButton.displayName = 'StatusBadgeButton'; StatusBadgeButton.displayName = 'StatusBadgeButton';

View File

@ -3,13 +3,8 @@ import React, { useMemo } from 'react';
import { getTooltipText } from './helpers'; import { getTooltipText } from './helpers';
import { useMemoizedFn } from 'ahooks'; import { useMemoizedFn } from 'ahooks';
import { StatusBadgeIndicator } from './StatusBadgeIndicator'; import { StatusBadgeIndicator } from './StatusBadgeIndicator';
import { Dropdown, DropdownItem, DropdownItems } from '@/components/ui/dropdown';
export const StatusDropdownContent: React.FC<{
isAdmin: boolean;
status: VerificationStatus;
onChangeStatus: (status: VerificationStatus) => void;
}> = React.memo(({ isAdmin, status, onChangeStatus }) => {
const items = useMemo(() => {
const statuses = [ const statuses = [
VerificationStatus.notRequested, VerificationStatus.notRequested,
VerificationStatus.requested, VerificationStatus.requested,
@ -17,16 +12,26 @@ export const StatusDropdownContent: React.FC<{
VerificationStatus.verified, VerificationStatus.verified,
VerificationStatus.backlogged VerificationStatus.backlogged
]; ];
const requiresAdminItems = [ const requiresAdminItems = [
VerificationStatus.inReview, VerificationStatus.inReview,
VerificationStatus.verified, VerificationStatus.verified,
VerificationStatus.backlogged VerificationStatus.backlogged
]; ];
return statuses.map((status, index) => {
export const StatusDropdownContent: React.FC<{
isAdmin: boolean;
status: VerificationStatus;
children: React.ReactNode;
onChangeStatus: (status: VerificationStatus) => void;
}> = React.memo(({ isAdmin, status, onChangeStatus, children }) => {
const items: DropdownItems<VerificationStatus> = useMemo(() => {
return statuses.map<DropdownItems<VerificationStatus>[number]>((status, index) => {
const requiresAdmin = requiresAdminItems.includes(status); const requiresAdmin = requiresAdminItems.includes(status);
return { return {
index, index,
label: getTooltipText(status), label: getTooltipText(status),
value: status,
icon: <StatusBadgeIndicator status={status} />, icon: <StatusBadgeIndicator status={status} />,
key: status, key: status,
disabled: requiresAdmin && !isAdmin, disabled: requiresAdmin && !isAdmin,
@ -39,5 +44,22 @@ export const StatusDropdownContent: React.FC<{
}); });
}, [isAdmin, status, onChangeStatus]); }, [isAdmin, status, onChangeStatus]);
return <div>StatusDropdownContent</div>; const onSelect = useMemoizedFn((item: DropdownItems[number]) => {
const _item = item as DropdownItem<VerificationStatus>;
onChangeStatus(_item.value as VerificationStatus);
}); });
return (
<Dropdown
emptyStateText="Nothing to see here..."
items={items}
onSelect={(v) => {
console.log(v);
}}
selectType="single"
menuHeader="Status">
{children}
</Dropdown>
);
});
StatusDropdownContent.displayName = 'StatusDropdownContent';

View File

@ -25,12 +25,12 @@ import { useDebounceSearch } from '@/hooks';
import Link from 'next/link'; import Link from 'next/link';
import { useHotkeys } from 'react-hotkeys-hook'; import { useHotkeys } from 'react-hotkeys-hook';
export interface DropdownItem { export interface DropdownItem<T = string> {
label: React.ReactNode | string; label: React.ReactNode | string;
truncate?: boolean; truncate?: boolean;
searchLabel?: string; // Used for filtering searchLabel?: string; // Used for filtering
secondaryLabel?: string; secondaryLabel?: string;
value: string; value: T;
showIndex?: boolean; showIndex?: boolean;
shortcut?: string; shortcut?: string;
onClick?: () => void; onClick?: () => void;
@ -47,14 +47,14 @@ export interface DropdownDivider {
type: 'divider'; type: 'divider';
} }
export type DropdownItems = (DropdownItem | DropdownDivider | React.ReactNode)[]; export type DropdownItems<T = string> = (DropdownItem<T> | DropdownDivider | React.ReactNode)[];
export interface DropdownProps extends DropdownMenuProps { export interface DropdownProps<T = string> extends DropdownMenuProps {
items: DropdownItems; items: DropdownItems;
selectType?: 'single' | 'multiple' | 'none'; selectType?: 'single' | 'multiple' | 'none';
menuHeader?: string | React.ReactNode; //if string it will render a search box menuHeader?: string | React.ReactNode; //if string it will render a search box
closeOnSelect?: boolean; closeOnSelect?: boolean;
onSelect?: (itemId: string) => void; onSelect?: (value: T) => void;
align?: 'start' | 'center' | 'end'; align?: 'start' | 'center' | 'end';
side?: 'top' | 'right' | 'bottom' | 'left'; side?: 'top' | 'right' | 'bottom' | 'left';
emptyStateText?: string; emptyStateText?: string;
@ -70,7 +70,7 @@ const dropdownItemKey = (item: DropdownItems[number], index: number) => {
export const Dropdown: React.FC<DropdownProps> = React.memo( export const Dropdown: React.FC<DropdownProps> = React.memo(
({ ({
items = [], items,
selectType = 'none', selectType = 'none',
menuHeader, menuHeader,
closeOnSelect = true, closeOnSelect = true,

View File

@ -34,7 +34,9 @@ export const Tooltip = React.memo<TooltipProps>(
return ( return (
<TooltipProvider delayDuration={delayDuration} skipDelayDuration={skipDelayDuration}> <TooltipProvider delayDuration={delayDuration} skipDelayDuration={skipDelayDuration}>
<TooltipBase open={open}> <TooltipBase open={open}>
<TooltipTrigger asChild>{children}</TooltipTrigger> <TooltipTrigger asChild>
<span className="inline-block">{children}</span>
</TooltipTrigger>
<TooltipContentBase align={align} side={side} sideOffset={sideOffset}> <TooltipContentBase align={align} side={side} sideOffset={sideOffset}>
<TooltipContent title={title} shortcut={shortcuts} /> <TooltipContent title={title} shortcut={shortcuts} />
</TooltipContentBase> </TooltipContentBase>

View File

@ -6,7 +6,8 @@ import type {
BusterChatMessage_fileMetadata, BusterChatMessage_fileMetadata,
BusterChatMessageReasoning_pills, BusterChatMessageReasoning_pills,
BusterChatMessageReasoning_Pill, BusterChatMessageReasoning_Pill,
BusterChatMessageReasoning_file BusterChatMessageReasoning_file,
BusterChatMessageReasoning_text
} from '@/api/asset_interfaces'; } from '@/api/asset_interfaces';
import { faker } from '@faker-js/faker'; import { faker } from '@faker-js/faker';
@ -29,7 +30,7 @@ const createMockResponseMessageText = (): BusterChatMessage_text => ({
}) })
}); });
const createMockResponseMessageThought = (): BusterChatMessageReasoning_pills => { const createMockResponseMessagePills = (): BusterChatMessageReasoning_pills => {
const randomPillCount = faker.number.int({ min: 0, max: 10 }); const randomPillCount = faker.number.int({ min: 0, max: 10 });
const fourRandomPills: BusterChatMessageReasoning_Pill[] = Array.from( const fourRandomPills: BusterChatMessageReasoning_Pill[] = Array.from(
{ length: randomPillCount }, { length: randomPillCount },
@ -119,6 +120,17 @@ const createMockReasoningMessageFile = (): BusterChatMessageReasoning_file => {
}; };
}; };
const createMockReasoningMessageText = (): BusterChatMessageReasoning_text => {
return {
id: faker.string.uuid(),
type: 'text',
message: faker.lorem.sentence(),
title: faker.lorem.words(4),
secondary_title: faker.lorem.sentence(),
status: 'loading'
};
};
export const MOCK_CHAT: BusterChat = { export const MOCK_CHAT: BusterChat = {
id: '0', id: '0',
title: 'Mock Chat', title: 'Mock Chat',
@ -130,11 +142,10 @@ export const MOCK_CHAT: BusterChat = {
request_message: createMockUserMessage(), request_message: createMockUserMessage(),
final_reasoning_message: null, final_reasoning_message: null,
reasoning: [ reasoning: [
...Array.from({ length: 1 }, () => createMockResponseMessageThought()), createMockReasoningMessageText(),
createMockReasoningMessageText(),
...Array.from({ length: 1 }, () => createMockResponseMessagePills()),
createMockReasoningMessageFile() createMockReasoningMessageFile()
// createMockReasoningMessageFile(),
// createMockResponseMessageThought(),
// createMockResponseMessageThought()
], ],
response_messages: [ response_messages: [
createMockResponseMessageText(), createMockResponseMessageText(),

View File

@ -1,6 +1,9 @@
import { MOCK_CHAT } from './MOCK_CHAT';
import { useChatIndividual } from './useChatIndividual'; import { useChatIndividual } from './useChatIndividual';
import { useMessageIndividual } from './useMessageIndividual'; import { useMessageIndividual } from './useMessageIndividual';
export * from './ChatProvider'; export * from './ChatProvider';
export { useChatIndividual, useMessageIndividual }; export { useChatIndividual, useMessageIndividual };
console.log(MOCK_CHAT);