Merge branch 'staging' into big-nate-bus-1815-pulled-in-reports-treated-as-dashboards

This commit is contained in:
Nate Kelley 2025-09-17 15:19:59 -06:00
commit e23fa1184c
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
5 changed files with 65 additions and 45 deletions

View File

@ -310,7 +310,7 @@ export const useAddAndRemoveAssetsFromCollection = () => {
const addAndRemoveAssetsToCollection = async (variables: {
collectionId: string;
assets: {
type: Exclude<ShareAssetType, 'collection'>;
type: ShareAssetType;
id: string;
}[];
}) => {

View File

@ -108,7 +108,7 @@ export const addAssetToCollection = async ({
}: {
id: string;
assets: {
type: Exclude<ShareAssetType, 'collection'>;
type: ShareAssetType;
id: string;
}[];
}) => {

View File

@ -18,6 +18,8 @@ import { useDebounce } from '@/hooks/useDebounce';
import { useMemoizedFn } from '@/hooks/useMemoizedFn';
import { formatDate } from '@/lib/date';
type SelectedAsset = { id: string; type: ShareAssetType };
export const AddToCollectionModal: React.FC<{
open: boolean;
onClose: () => void;
@ -35,7 +37,10 @@ export const AddToCollectionModal: React.FC<{
num_results: 100,
});
const [selectedAssets, setSelectedAssets] = useState<string[]>([]);
const [selectedAssets, setSelectedAssets] = useState<SelectedAsset[]>([]);
const selectedAssetIds = useMemo(() => {
return selectedAssets.map((asset) => asset.id);
}, [selectedAssets]);
const columns = useMemo<InputSelectModalProps<BusterSearchResult>['columns']>(
() => [
@ -78,46 +83,48 @@ export const AddToCollectionModal: React.FC<{
);
}, [searchResults]);
const handleAddAndRemoveMetrics = useMemoizedFn(async () => {
const keyedAssets = rows.reduce<
Record<string, { type: Exclude<ShareAssetType, 'collection'>; id: string }>
>((acc, asset) => {
if (asset.data?.type && asset.data?.type !== 'collection') {
acc[asset.id] = { type: asset.data?.type, id: asset.id };
}
return acc;
}, {});
const createKeySearchResultMap = useMemoizedFn(() => {
const map = new Map<string, SelectedAsset>();
rows.forEach((asset) => {
if (asset.data?.type) map.set(asset.id, { type: asset.data?.type, id: asset.id });
});
return map;
});
const assets = selectedAssets.map<{
type: Exclude<ShareAssetType, 'collection'>;
id: string;
}>((asset) => ({
id: asset,
type: keyedAssets[asset].type,
}));
const handleAddAndRemoveMetrics = useMemoizedFn(async () => {
await addAndRemoveAssetsFromCollection({
collectionId,
assets,
assets: selectedAssets,
});
onClose();
});
const handleSelectChange = useMemoizedFn((assets: string[]) => {
const selectedAssets: SelectedAsset[] = [];
const keySearchResultMap = createKeySearchResultMap();
assets.forEach((assetId) => {
const asset = keySearchResultMap.get(assetId);
if (asset) selectedAssets.push({ id: assetId, type: asset.type });
});
setSelectedAssets(selectedAssets);
});
const originalIds = useMemo(() => {
return collection?.assets?.map((asset) => asset.id) || [];
}, [collection?.assets]);
const isSelectedChanged = useMemo(() => {
const newIds = selectedAssets;
const newIds = selectedAssetIds;
return originalIds.length !== newIds.length || originalIds.some((id) => !newIds.includes(id));
}, [originalIds, selectedAssets]);
}, [originalIds, selectedAssetIds]);
const removedAssetCount = useMemo(() => {
return originalIds.filter((id) => !selectedAssets.includes(id)).length;
}, [originalIds, selectedAssets]);
return originalIds.filter((id) => !selectedAssetIds.includes(id)).length;
}, [originalIds, selectedAssetIds]);
const addedAssetCount = useMemo(() => {
return selectedAssets.filter((id) => !originalIds.includes(id)).length;
}, [originalIds, selectedAssets]);
return selectedAssetIds.filter((id) => !originalIds.includes(id)).length;
}, [originalIds, selectedAssetIds]);
const primaryButtonText = useMemo(() => {
if (!isFetchedCollection) {
@ -202,8 +209,11 @@ export const AddToCollectionModal: React.FC<{
useLayoutEffect(() => {
if (isFetchedCollection) {
const assets = collection?.assets?.map((asset) => asset.id) || [];
setSelectedAssets(assets);
const assets = collection?.assets?.map((asset) => ({
id: asset.id,
type: asset.asset_type,
}));
setSelectedAssets(assets || []);
}
}, [isFetchedCollection, collection?.assets]);
@ -214,8 +224,8 @@ export const AddToCollectionModal: React.FC<{
onClose={onClose}
columns={columns}
rows={rows}
onSelectChange={setSelectedAssets}
selectedRowKeys={selectedAssets}
onSelectChange={handleSelectChange}
selectedRowKeys={selectedAssetIds}
footer={footer}
emptyState={emptyState}
searchText={searchTerm}

View File

@ -17,7 +17,7 @@ const useSupabaseContextInternal = ({ supabaseSession }: SupabaseContextType) =>
const refreshTimerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
const busterUser = useGetUserBasicInfo();
const [supabaseUser, setSupabaseUser] = useState<null | User>(null);
const [accessToken, setAccessToken] = useState(supabaseSession?.accessToken);
const [accessToken, setAccessToken] = useState(supabaseSession?.accessToken || '');
const isAnonymousUser: boolean = !busterUser?.id || supabaseUser?.is_anonymous === true;

View File

@ -1,5 +1,5 @@
import type { ModelMessage } from 'ai';
import { and, eq, isNull } from 'drizzle-orm';
import { and, eq, isNull, lte } from 'drizzle-orm';
import { z } from 'zod';
import { db } from '../../connection';
import { messages } from '../../schema';
@ -244,13 +244,14 @@ function convertToolResultPart(part: unknown): unknown | unknown[] | null {
return part;
}
// Helper function to get chatId from messageId
async function getChatIdFromMessage(messageId: string): Promise<string> {
let messageResult: Array<{ chatId: string }>;
// Helper function to get chatId and createdAt from messageId
async function getMessageInfo(messageId: string): Promise<{ chatId: string; createdAt: string }> {
let messageResult: Array<{ chatId: string; createdAt: string }>;
try {
messageResult = await db
.select({
chatId: messages.chatId,
createdAt: messages.createdAt,
})
.from(messages)
.where(and(eq(messages.id, messageId), isNull(messages.deletedAt)))
@ -266,11 +267,14 @@ async function getChatIdFromMessage(messageId: string): Promise<string> {
throw new Error('Message not found or has been deleted');
}
return messageRow.chatId;
return messageRow;
}
// Helper function to get all messages for a chat
async function getAllMessagesForChat(chatId: string): Promise<
// Helper function to get messages for a chat up to a specific timestamp
async function getMessagesUpToTimestamp(
chatId: string,
upToTimestamp: string
): Promise<
Array<{
id: string;
rawLlmMessages: unknown;
@ -293,7 +297,13 @@ async function getAllMessagesForChat(chatId: string): Promise<
isCompleted: messages.isCompleted,
})
.from(messages)
.where(and(eq(messages.chatId, chatId), isNull(messages.deletedAt)))
.where(
and(
eq(messages.chatId, chatId),
isNull(messages.deletedAt),
lte(messages.createdAt, upToTimestamp)
)
)
.orderBy(messages.createdAt);
} catch (dbError) {
throw new Error(
@ -315,9 +325,9 @@ export type ChatConversationHistoryInput = z.infer<typeof ChatConversationHistor
export type ChatConversationHistoryOutput = z.infer<typeof ChatConversationHistoryOutputSchema>;
/**
* Get complete conversation history for a chat from any message in that chat
* Get conversation history for a chat up to and including a specific message
* Finds the chat from the given messageId, then merges and deduplicates all rawLlmMessages
* from all messages in the chat to create a complete conversation history
* from messages up to and including the specified message to create the conversation history
*/
export async function getChatConversationHistory(
input: ChatConversationHistoryInput
@ -326,11 +336,11 @@ export async function getChatConversationHistory(
// Validate input
const validatedInput = ChatConversationHistoryInputSchema.parse(input);
// Get chatId from messageId
const chatId = await getChatIdFromMessage(validatedInput.messageId);
// Get chatId and timestamp from messageId
const { chatId, createdAt } = await getMessageInfo(validatedInput.messageId);
// Get all messages for this chat
const chatMessages = await getAllMessagesForChat(chatId);
// Get all messages for this chat up to and including the current message
const chatMessages = await getMessagesUpToTimestamp(chatId, createdAt);
// Collect all rawLlmMessages from all messages
const allRawMessages: unknown[] = [];