This repository has been archived by the owner on Jan 5, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 398
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #411 from openchatai/ui-enhancements
- Loading branch information
Showing
9 changed files
with
396 additions
and
324 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
'use client'; | ||
import { useSearchModal } from '@/app/_store/searchModal'; | ||
import { Button } from '@/components/ui/button'; | ||
import { Search } from 'lucide-react' | ||
import React from 'react' | ||
|
||
|
||
export function SearchBtn() { | ||
const [, setOpen] = useSearchModal(); | ||
return ( | ||
<Button | ||
size='fit' | ||
variant='secondary' | ||
className='rounded-full p-2.5' | ||
onClick={() => setOpen(true)} | ||
> | ||
<Search className="h-4 w-4" /> | ||
</Button> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
260 changes: 260 additions & 0 deletions
260
dashboard/app/(main)/create/copilot/_parts/DefineActionsStep.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,260 @@ | ||
'use client'; | ||
import { atom, useAtom } from "jotai"; | ||
import { useAsyncFn } from "react-use"; | ||
import { useWizard } from "react-use-wizard"; | ||
import { revalidateActions, useCreateCopilot } from "./CreateCopilotProvider"; | ||
import useSWR from "swr"; | ||
import _ from "lodash"; | ||
import { toast } from "@/components/ui/use-toast"; | ||
import { AlertDialog, AlertDialogContent, AlertDialogCancel, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog"; | ||
import { createActionByBotId, getActionsByBotId, importActionsFromSwagger } from "@/data/actions"; | ||
import Link from "next/link"; | ||
import { DropZone } from "@/components/domain/DropZone"; | ||
import { Button } from "@/components/ui/button"; | ||
import { Plus, Trash2, UploadCloud } from "lucide-react"; | ||
import { EmptyBlock } from "@/components/domain/EmptyBlock"; | ||
import { ActionForm } from "@/components/domain/action-form/ActionForm"; | ||
import { methodVariants } from "@/components/domain/MethodRenderer"; | ||
import { cn } from "@/lib/utils"; | ||
|
||
function GetActionsFromSwagger() { | ||
const { state: { swaggerFiles }, dispatch } = useCreateCopilot(); | ||
return <div> | ||
<div className="my-5"> | ||
<DropZone | ||
multiple={false} | ||
maxFiles={1} | ||
accept={{ json: ["application/json"] }} | ||
value={swaggerFiles || []} | ||
onChange={(files) => { | ||
dispatch({ type: "ADD_SWAGGER", payload: files }); | ||
}} | ||
/> | ||
</div> | ||
<div className="mb-8 mt-4 flex items-center justify-between space-x-6"> | ||
<div> | ||
<div className="mb-1 text-sm font-medium text-slate-800"> | ||
Important Instructions | ||
</div> | ||
<div className="text-xs"> | ||
<ul> | ||
<li> | ||
✅ Make sure each{" "} | ||
<strong>endpoint have description and operation id</strong>, | ||
results will be significantly better with a good description | ||
</li> | ||
<li> | ||
✅ Make sure that the swagger file is valid, the system | ||
might not be able to parse invalid files,{" "} | ||
<Link href="https://editor.swagger.io/" target="_blank"> | ||
use this tool validate your schema | ||
</Link> | ||
</li> | ||
<li> | ||
✅ Do not add any Authorization layers, we will show you how | ||
to authorize your own requests by yourself | ||
</li> | ||
<li> | ||
✅ This is a *very* new product, so many things does not make | ||
sense/work at this stage{" "} | ||
</li> | ||
</ul> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
} | ||
|
||
|
||
const formDialog = atom({ | ||
swagger: false, | ||
manually: false | ||
}) | ||
|
||
|
||
export function DefineActionsStep() { | ||
const { nextStep, previousStep } = useWizard(); | ||
const [state, $importActionsFromSwagger] = useAsyncFn(importActionsFromSwagger) | ||
const [addActionState, $addAction] = useAsyncFn(createActionByBotId) | ||
const { | ||
state: { swaggerFiles, createdCopilot }, | ||
dispatch, | ||
} = useCreateCopilot(); | ||
const [dialogs, setDialogs] = useAtom(formDialog) | ||
const { data: actions } = useSWR(createdCopilot ? (createdCopilot?.id + '/actions') : null, async () => createdCopilot?.id ? await getActionsByBotId(createdCopilot?.id) : null) | ||
async function addActionFromSwagger() { | ||
const swaggerFile = _.first(swaggerFiles); | ||
if (swaggerFile && createdCopilot) { | ||
const response = await $importActionsFromSwagger(createdCopilot.id, swaggerFile) | ||
if (response.data) { | ||
toast({ | ||
title: "Actions imported successfully", | ||
description: "We have imported your actions successfully", | ||
variant: "success", | ||
}); | ||
} | ||
// reset swagger files | ||
dispatch({ | ||
type: "ADD_SWAGGER", | ||
payload: [], | ||
}); | ||
revalidateActions(createdCopilot.id) | ||
setDialogs({ | ||
...dialogs, | ||
swagger: false | ||
}) | ||
} | ||
} | ||
return ( | ||
<div className="relative p-1"> | ||
<div className="mb-5 flex items-center justify-between"> | ||
<h2 className="text-3xl font-bold text-accent-foreground"> | ||
Define your actions ✨ | ||
</h2> | ||
</div> | ||
|
||
<p className="mb-2"> | ||
You copilot will use these APIs to communicate with your product and | ||
execute actions | ||
</p> | ||
<div className="flex items-center mb-2 space-x-2 justify-end"> | ||
{/* Via Form */} | ||
<AlertDialog open={dialogs.manually} onOpenChange={(open) => setDialogs({ | ||
...dialogs, | ||
manually: open, | ||
})}> | ||
<AlertDialogContent> | ||
<AlertDialogHeader className="flex items-center justify-between w-full flex-row"> | ||
<AlertDialogTitle className="flex-1 text-lg font-bold"> | ||
Define API action | ||
</AlertDialogTitle> | ||
</AlertDialogHeader> | ||
<ActionForm | ||
onSubmit={async (values) => { | ||
if (createdCopilot) { | ||
const { data } = await $addAction(createdCopilot.id, values); | ||
if (data) { | ||
toast({ | ||
title: "Action created successfully", | ||
description: "We have created your action successfully", | ||
variant: "success", | ||
}); | ||
setDialogs({ | ||
...dialogs, | ||
manually: false | ||
}) | ||
revalidateActions(createdCopilot.id) | ||
} | ||
} | ||
}} | ||
footer={ | ||
() => <AlertDialogFooter> | ||
<AlertDialogCancel asChild> | ||
<Button variant='outline'>Cancel</Button> | ||
</AlertDialogCancel> | ||
<Button type="submit" loading={addActionState.loading}> | ||
Create Action | ||
</Button> | ||
</AlertDialogFooter> | ||
} /> | ||
</AlertDialogContent> | ||
<AlertDialogTrigger asChild> | ||
<Button className="space-x-1" size='xs' variant='secondary'> | ||
<Plus className="w-4 h-4" /> | ||
<span> | ||
Add action manually | ||
</span> | ||
</Button> | ||
</AlertDialogTrigger> | ||
</AlertDialog> | ||
{/* Via swagger Def file */} | ||
<AlertDialog open={dialogs.swagger} onOpenChange={(open) => setDialogs({ | ||
...dialogs, | ||
swagger: open, | ||
})}> | ||
<AlertDialogContent> | ||
<AlertDialogHeader> | ||
<AlertDialogTitle className="flex-1 text-lg font-bold"> | ||
Import from Swagger | ||
</AlertDialogTitle> | ||
</AlertDialogHeader> | ||
<GetActionsFromSwagger /> | ||
<AlertDialogFooter> | ||
<AlertDialogCancel asChild> | ||
<Button variant='outline'>Cancel</Button> | ||
</AlertDialogCancel> | ||
<Button onClick={addActionFromSwagger} loading={state.loading}>Import</Button> | ||
</AlertDialogFooter> | ||
</AlertDialogContent> | ||
<AlertDialogTrigger asChild > | ||
<Button className="space-x-1" size='xs' variant='secondary'> | ||
<UploadCloud className="w-4 h-4" /> | ||
<span> | ||
Import actions from Swagger file | ||
</span> | ||
</Button> | ||
</AlertDialogTrigger> | ||
</AlertDialog> | ||
</div> | ||
<div className="flex items-start flex-col gap-2 overflow-auto max-h-60 px-2 py-1"> | ||
{ | ||
_.isEmpty(actions?.data) ? | ||
<div className="mx-auto"> | ||
<EmptyBlock> | ||
<div className="text-center text-sm"> | ||
<span className="block"> | ||
No actions added yet. | ||
</span> | ||
<span className="block"> | ||
You can add one manually or import a bunch from swagger file | ||
</span> | ||
</div> | ||
</EmptyBlock> | ||
</div> | ||
: | ||
_.map(actions?.data, (endpoint, index) => { | ||
return <div key={index} className="w-full p-2 shrink-0 flex overflow-hidden max-w-full gap-4 items-center justify-between border border-border transition-colors rounded-lg"> | ||
<div className="flex-1 flex items-center justify-start overflow-hidden shrink-0"> | ||
<div className="flex items-center gap-5 overflow-hidden shrink-0"> | ||
<span className={cn(methodVariants({ | ||
method: endpoint.request_type | ||
}))}> | ||
{endpoint.request_type} | ||
</span> | ||
<p className="flex-1 line-clamp-1 overflow-ellipsis font-medium text-xs"> | ||
{endpoint.api_endpoint || endpoint.name} | ||
</p> | ||
</div> | ||
</div> | ||
<div className="space-x-2"> | ||
<button className="text-destructive"> | ||
<Trash2 className="w-4 h-4" onClick={() => confirm("are you sure")} /> | ||
</button> | ||
</div> | ||
</div> | ||
}) | ||
} | ||
</div> | ||
|
||
<footer className="flex w-full items-center justify-between gap-5 pt-5"> | ||
<Button | ||
variant="ghost" | ||
onClick={previousStep} | ||
className="flex items-center justify-center gap-1 underline" | ||
> | ||
Back | ||
</Button> | ||
{createdCopilot && ( | ||
<Button | ||
variant='ghost' | ||
className="flex items-center justify-center gap-1 underline" | ||
onClick={nextStep}> | ||
{ | ||
_.isEmpty(actions?.data) ? "Skip for now ►" : "Next" | ||
} | ||
</Button> | ||
)} | ||
</footer> | ||
</div > | ||
); | ||
} |
Oops, something went wrong.