diff --git a/e2e/playwright/projects.spec.ts b/e2e/playwright/projects.spec.ts index 22e23525c..1870c1ef3 100644 --- a/e2e/playwright/projects.spec.ts +++ b/e2e/playwright/projects.spec.ts @@ -1343,7 +1343,7 @@ test( await test.step('should be shorted by modified initially', async () => { const lastModifiedButton = page.getByRole('button', { - name: 'Last Modified', + name: 'Age', }) await expect(lastModifiedButton).toBeVisible() await expect(lastModifiedButton.getByLabel('arrow down')).toBeVisible() @@ -1364,7 +1364,7 @@ test( await test.step('Reverse modified order', async () => { const lastModifiedButton = page.getByRole('button', { - name: 'Last Modified', + name: 'Age', }) await lastModifiedButton.click() await expect(lastModifiedButton).toBeVisible() diff --git a/src/components/HomeSearchBar.tsx b/src/components/HomeSearchBar.tsx index ace2c3a3c..5b1422278 100644 --- a/src/components/HomeSearchBar.tsx +++ b/src/components/HomeSearchBar.tsx @@ -5,7 +5,7 @@ import { useHotkeys } from 'react-hotkeys-hook' import { CustomIcon } from '@src/components/CustomIcon' import type { Project } from '@src/lib/project' -import type { Prompt } from '@src/machines/mlEphantManagerMachine' +import type { Prompt } from '@src/lib/prompt' export type HomeItem = Project | Prompt export type HomeItems = Project[] | Prompt[] @@ -17,7 +17,7 @@ export const areHomeItemsProjects = (items: HomeItems): items is Project[] => { export const areHomeItemsPrompts = (items: HomeItems): items is Prompt[] => { const item = items[0] - return item !== undefined && typeof item.prompt === 'string' + return item !== undefined && 'prompt' in item } export function useHomeSearch(initialSearchResults: HomeItems) { @@ -34,6 +34,8 @@ export function useHomeSearch(initialSearchResults: HomeItems) { ? 'name' : 'prompt' + // Fuse is not happy with HomeItems + // @ts-expect-error const fuse = new Fuse(items, { keys: [{ name: nameKeyToMatchAgainst, weight: 0.7 }], includeScore: true, diff --git a/src/components/PromptCard.tsx b/src/components/PromptCard.tsx index 790c7ba59..4e18b2b0b 100644 --- a/src/components/PromptCard.tsx +++ b/src/components/PromptCard.tsx @@ -1,5 +1,5 @@ import ms from 'ms' -import { Prompt } from '@src/lib/prompt' +import type { Prompt } from '@src/lib/prompt' interface PromptCardProps extends Prompt { onAction?: (id: Prompt['id']) => void @@ -8,16 +8,27 @@ interface PromptCardProps extends Prompt { } export const PromptFeedback = (props: { + id: Prompt['id'] selected: Prompt['feedback'] onFeedback: (id: Prompt['id'], feedback: Prompt['feedback']) => void }) => { - const cssUp = 'border-green-500' - const cssDown = 'border-red-500' + const cssUp = 'border-green-300' + const cssDown = 'border-red-300' return (
- - + +
) } @@ -45,8 +56,9 @@ export const PromptCard = (props: PromptCardProps) => {
props.onFeedback?.(...args)} />
diff --git a/src/lib/prompt.ts b/src/lib/prompt.ts index a0e5c75e6..d888a537b 100644 --- a/src/lib/prompt.ts +++ b/src/lib/prompt.ts @@ -1,6 +1,6 @@ import type { Models } from '@kittycad/lib' -export type Prompt = Models['TextToCad_Type'] +export type Prompt = Models['TextToCad_type'] // export interface TextToCad_type { // code?: string; @@ -100,21 +100,22 @@ export const generateFakeSubmittedPrompt = () => ({ created_at: new Date(Math.random() * 100000000).toISOString(), error: Math.random().toString(), // declare type MlFeedback_type = 'thumbs_up' | 'thumbs_down' | 'accepted' | 'rejected'; - feedback: 'thumbs_up', + feedback: 'thumbs_up' as Prompt['feedback'], id: Math.random().toString(), kcl_version: Math.random().toString(), - // export declare type TextToCadModel_type = 'cad' | 'kcl' | 'kcl_iteration'; model : 'kcl', model_version: Math.random().toString(), + // export declare type TextToCadModel_type = 'cad' | 'kcl' | 'kcl_iteration'; model : 'kcl', + model: 'kcl' as Prompt['model'], // export declare type FileExportFormat_type = 'fbx' | 'glb' | 'gltf' | 'obj' | 'ply' | 'step' | 'stl'; - output_format: 'glb', + output_format: 'glb' as Prompt['output_format'], outputs: { [Math.random().toString()]: Math.random().toString(), }, prompt: PROMPTS[parseInt((Math.random() * 10).toString()[0])], started_at: new Date(Math.random()).toISOString(), // declare type ApiCallStatus_type = 'queued' | 'uploaded' | 'in_progress' | 'completed' | 'failed'; - status: 'completed', - updated_at: Math.random(), + status: 'completed' as Prompt['status'], + updated_at: Math.random().toString(), // declare type ApiTokenUuid_type = string; user_id: Math.random().toString(), }) diff --git a/src/lib/sorting.ts b/src/lib/sorting.ts index 02af6d09b..d7e4ec217 100644 --- a/src/lib/sorting.ts +++ b/src/lib/sorting.ts @@ -1,5 +1,6 @@ import type { CustomIconName } from '@src/components/CustomIcon' import type { Project } from '@src/lib/project' +import type { Prompt } from '@src/lib/prompt' const DESC = ':desc' @@ -25,7 +26,7 @@ export function getNextSearchParams(currentSort: string, newSort: string) { } } -export function getSortFunction(sortBy: string) { +export function getProjectSortFunction(sortBy: string) { const sortByName = (a: Project, b: Project) => { if (a.name && b.name) { return sortBy.includes('desc') @@ -52,3 +53,33 @@ export function getSortFunction(sortBy: string) { return sortByModified } } + +// Below is to keep the same behavior as above but for prompts. +// Do NOT take it as actually "sort by modified" but more like "sort by time". +export function getPromptSortFunction(sortBy: string) { + const sortByName = (a: Prompt, b: Prompt) => { + if (a.prompt && b.prompt) { + return sortBy.includes('desc') + ? a.prompt.localeCompare(b.prompt) + : b.prompt.localeCompare(a.prompt) + } + return 0 + } + + const sortByModified = (a: Prompt, b: Prompt) => { + if (a.created_at && b.created_at) { + const aDate = new Date(a.created_at) + const bDate = new Date(b.created_at) + return !sortBy || sortBy.includes('desc') + ? bDate.getTime() - aDate.getTime() + : aDate.getTime() - bDate.getTime() + } + return 0 + } + + if (sortBy?.includes('name')) { + return sortByName + } else { + return sortByModified + } +} diff --git a/src/machines/mlEphantManagerMachine.ts b/src/machines/mlEphantManagerMachine.ts index 7aa4f08df..92a71d63d 100644 --- a/src/machines/mlEphantManagerMachine.ts +++ b/src/machines/mlEphantManagerMachine.ts @@ -1,4 +1,6 @@ +import { setup, fromPromise } from 'xstate' import type { Prompt } from '@src/lib/prompt' +import { generateFakeSubmittedPrompt } from '@src/lib/prompt' export enum MlEphantManagerTransitionStates { GetPromptsThatCreatedProjects = 'get-prompts-that-created-projects', @@ -47,9 +49,9 @@ export enum MlEphantManagerStates { } export interface MlEphantManagerContext { - promptsThatCreatedProjects: Map + promptsThatCreatedProjects: Map // If no project is selected: undefined. - promptsBelongingToProject?: Map + promptsBelongingToProject?: Map } export const mlEphantDefaultContext = Object.freeze({ @@ -58,13 +60,25 @@ export const mlEphantDefaultContext = Object.freeze({ hasPendingPrompts: false, }) -const machine = setup({ +export const mlEphantManagerMachine = setup({ types: { context: {} as MlEphantManagerContext, events: {} as MlEphantManagerEvents, }, + actors: { + [MlEphantManagerTransitionStates.GetPromptsThatCreatedProjects]: + fromPromise(async function (args) { + console.log(arguments) + return { + promptsThatCreatedProjects: new Array(13) + .fill(undefined) + .map(generateFakeSubmittedPrompt), + } + }), + }, }).createMachine({ initial: MlEphantManagerStates.Idle, + context: mlEphantDefaultContext, states: { [MlEphantManagerStates.Idle]: { on: { @@ -77,15 +91,8 @@ const machine = setup({ states: { [MlEphantManagerTransitionStates.GetPromptsThatCreatedProjects]: { invoke: { - input: (args) => args, - src: fromPromise(async function (args) { - console.log(arguments) - return { - promptsThatCreatedProjects: new Array(13) - .fill(undefined) - .map(generateFakeSubmittedPrompt), - } - }), + input: (args: any) => args, + src: MlEphantManagerTransitionStates.GetPromptsThatCreatedProjects, onDone: { target: MlEphantManagerStates.Idle }, onError: { target: MlEphantManagerStates.Idle }, }, diff --git a/src/routes/Home.tsx b/src/routes/Home.tsx index 772b82f9b..320279d73 100644 --- a/src/routes/Home.tsx +++ b/src/routes/Home.tsx @@ -17,10 +17,10 @@ import { PromptCard } from '@src/components/PromptCard' import { HomeSearchBar, useHomeSearch, - HomeItems, areHomeItemsProjects, areHomeItemsPrompts, } from '@src/components/HomeSearchBar' +import type { HomeItems } from '@src/components/HomeSearchBar' import { BillingDialog } from '@src/components/BillingDialog' import { useQueryParamEffects } from '@src/hooks/useQueryParamEffects' import { useMenuListener } from '@src/hooks/useMenu' @@ -32,7 +32,8 @@ import type { Prompt } from '@src/lib/prompt' import { generateFakeSubmittedPrompt } from '@src/lib/prompt' import { getNextSearchParams, - getSortFunction, + getProjectSortFunction, + getPromptSortFunction, getSortIcon, } from '@src/lib/sorting' import { reportRejection } from '@src/lib/trap' @@ -216,7 +217,7 @@ const Home = () => { } ) const projects = useFolders() - const prompts = [ + const prompts: Prompt[] = [ generateFakeSubmittedPrompt(), generateFakeSubmittedPrompt(), generateFakeSubmittedPrompt(), @@ -228,7 +229,7 @@ const Home = () => { const [tabSelected, setTabSelected] = useState( HomeTabKeys.Projects ) - const [items, setItems] = useState(projects) + const [items, setItems] = useState(projects) const [searchParams, setSearchParams] = useSearchParams() const { searchResults, query, searchAgainst } = useHomeSearch(projects) const sortBy = searchParams.get('sort_by') ?? 'modified:desc' @@ -484,6 +485,8 @@ function HomeTab(props: HomeTabProps) {
{el.name}
@@ -565,7 +568,7 @@ function HomeHeader({ : '', }} > - Last Modified + Age @@ -653,18 +656,37 @@ function HomeItemsArea(props: HomeItemsAreaProps) { ) } -interface ResultGridProjectsProps { +interface ResultGridPromptsProps { searchResults: Prompt[] query: string sortBy: string } -function ResultGridPrompts(props) { +function ResultGridPrompts(props: ResultGridPromptsProps) { + // Maybe consider lifting this higher but I see no reason at the moment + const onAction = (...args: any) => { + console.log(args) + } + const onDelete = (...args: any) => { + console.log(args) + } + const onFeedback = (...args: any) => { + console.log(args) + } + return (
- {props.searchResults.map((prompt) => ( - - ))} + {props.searchResults + .sort(getPromptSortFunction(props.sortBy)) + .map((prompt: Prompt) => ( + + ))}
) } @@ -686,8 +708,8 @@ function ResultGridProjects(props: ResultGridProjectsProps) { <> {props.searchResults.length > 0 ? (
    - {(props.searchResults ?? []) - .sort(getSortFunction(props.sortBy)) + {props.searchResults + .sort(getProjectSortFunction(props.sortBy)) .map((item) => ( No results found - {items.length === 0 + {props.searchResults.length === 0 ? ', ready to make your first one?' - : ` with the search term "${query}"`} + : ` with the search term "${props.query}"`}

    )}