Skip to content

Commit

Permalink
feat: add assistants
Browse files Browse the repository at this point in the history
  • Loading branch information
mikbry authored Feb 20, 2024
2 parents c8f6d26 + 022fc9e commit a5d781f
Show file tree
Hide file tree
Showing 24 changed files with 810 additions and 38 deletions.
38 changes: 37 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

115 changes: 115 additions & 0 deletions webapp/components/assistants/Assistant.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// Copyright 2024 mik
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import { Bug, Settings2 } from 'lucide-react';
import useTranslation from '@/hooks/useTranslation';
import logger from '@/utils/logger';
import { useAssistantStore } from '@/stores';
import RecordView from '../common/RecordView';
import { Button } from '../ui/button';
import { ScrollArea } from '../ui/scroll-area';
import Parameter, { ParameterValue } from '../common/Parameter';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '../ui/tabs';

export type AssistantProps = {
assistantId?: string;
};

export default function AssistantView({ assistantId }: AssistantProps) {
const { t } = useTranslation();
const { getAssistant, updateAssistant } = useAssistantStore();

const assistant = getAssistant(assistantId);

logger.info('Assistant', assistantId);

const handleUpdateParameter = (name: string, value: ParameterValue) => {
if (assistant) {
updateAssistant({ ...assistant, [name]: value });
}
};

return (
<Tabs defaultValue="settings" className="h-full">
<RecordView
title={
assistant ? (
<div className="flex flex-row items-center gap-4">
<div>{assistant.name}</div>
<TabsList className="gap-4">
<TabsTrigger value="settings">
<Settings2 className="mr-2 h-4 w-4" strokeWidth={1.5} />
{t('Settings')}
</TabsTrigger>
<TabsTrigger value="debug">
<Bug className="mr-2 h-4 w-4" strokeWidth={1.5} />
{t('Logs')}
</TabsTrigger>
</TabsList>
</div>
) : (
'Assistant'
)
}
selectedId={assistantId}
toolbar={
assistantId && (
<div>
<Button
variant="ghost"
size="sm"
onClick={(e) => {
e.preventDefault();
if (assistant) {
updateAssistant({ ...assistant, disabled: !assistant.disabled });
}
}}
>
{assistant?.disabled ? t('Enable') : t('Disable')}
</Button>
</div>
)
}
>
{assistant && (
<>
<TabsContent value="settings" className="h-full">
<ScrollArea className="h-full">
<div className="flex flex-col items-center gap-2 px-8 py-4 text-sm">
<Parameter
title={t('Name')}
name="name"
value={assistant?.name}
type="text"
onChange={handleUpdateParameter}
/>
<Parameter
title={t('Description')}
name="description"
value={assistant?.description}
type="large-text"
onChange={handleUpdateParameter}
/>
</div>
</ScrollArea>
</TabsContent>
<TabsContent value="settings" className="h-full">
<div>TODO</div>
</TabsContent>
</>
)}
</RecordView>
</Tabs>
);
}
127 changes: 127 additions & 0 deletions webapp/components/assistants/Explorer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// Copyright 2024 mik
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import { useContext } from 'react';
import { useRouter } from 'next/router';
import { Plus } from 'lucide-react';
import logger from '@/utils/logger';
import { useAssistantStore } from '@/stores';
import { Assistant, Ui } from '@/types';
import useTranslation from '@/hooks/useTranslation';
import { ModalIds } from '@/modals';
import { ModalData, ModalsContext } from '@/context/modals';
import { Page } from '@/types/ui';
import Explorer, { ExplorerList } from '../common/Explorer';
import { Button } from '../ui/button';

export default function AssistantsExplorer({
selectedAssistantId,
}: {
selectedAssistantId?: string;
}) {
const router = useRouter();
const { t } = useTranslation();
const { showModal } = useContext(ModalsContext);
logger.info('AssistantsExplorer', selectedAssistantId);
const { assistants, getAssistant, createAssistant, updateAssistant, deleteAssistant } =
useAssistantStore();

const handleSelectItem = (id: string) => {
logger.info(`onSelectItem ${id}`);
const route = Ui.Page.Assistants;
router.push(`${route}/${id}`);
};

const handleToggle = (id: string) => {
const assistant = getAssistant(id);
if (assistant) {
assistant.disabled = !assistant.disabled;
updateAssistant(assistant);
}
logger.info(`onToggle ${id}`);
};

const handleDelete = async (action: string, data: ModalData) => {
const { id } = data.item;
if (action === 'Delete') {
logger.info(`onDelete ${id}`);
deleteAssistant(id);
if (selectedAssistantId && selectedAssistantId === id) {
router.replace(Page.Assistants);
}
}
};

const handleToDelete = (id: string) => {
logger.info(`onToDelete${id}`);
const assistant = getAssistant(id);
if (assistant) {
showModal(ModalIds.DeleteItem, { item: assistant, onAction: handleDelete });
}
};

const menu: Ui.MenuItem[] = [
{
label: t('Disable'),
onSelect: (data: string) => {
handleToggle(data);
},
},
{
label: t('Delete'),
onSelect: handleToDelete,
},
];
const menuDisabled: Ui.MenuItem[] = [
{
label: t('Enable'),
onSelect: (data: string) => {
logger.info(`enable ${data}`);
handleToggle(data);
},
},
{
label: t('Delete'),
onSelect: handleToDelete,
},
];

return (
<Explorer
title="Assistants"
toolbar={
<Button
aria-label={t('New Assistant')}
title={t('New Assistant')}
variant="ghost"
size="icon"
onClick={(e) => {
e.preventDefault();
const assistant = createAssistant(`Assistant ${assistants.length + 1}`);
handleSelectItem(assistant.id);
}}
>
<Plus className="mr-2 h-4 w-4" strokeWidth={1.5} />
</Button>
}
>
<ExplorerList<Assistant>
selectedId={selectedAssistantId}
items={assistants}
onSelectItem={handleSelectItem}
menu={(assistant) => (assistant.disabled ? menuDisabled : menu)}
/>
</Explorer>
);
}
35 changes: 35 additions & 0 deletions webapp/components/assistants/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright 2024 mik
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '../ui/resizable';
import Explorer from './Explorer';
import Assistant from './Assistant';

export type AssistantProps = {
selectedAssistantId?: string;
};

export default function Assistants({ selectedAssistantId }: AssistantProps) {
return (
<ResizablePanelGroup direction="horizontal">
<ResizablePanel defaultSize={20}>
<Explorer selectedAssistantId={selectedAssistantId} />
</ResizablePanel>
<ResizableHandle />
<ResizablePanel>
<Assistant assistantId={selectedAssistantId} />
</ResizablePanel>
</ResizablePanelGroup>
);
}
Loading

0 comments on commit a5d781f

Please sign in to comment.