diff --git a/apps/server/utils/configs/levanion_configs.py b/apps/server/utils/configs/levanion_configs.py index 52cb44b51..ee7631acf 100644 --- a/apps/server/utils/configs/levanion_configs.py +++ b/apps/server/utils/configs/levanion_configs.py @@ -5,7 +5,7 @@ "agent": "Agent", "team": "Team", "datasource": "Data sources", - "models": "Models", + "models": "Model", "discovery": "Discovery", "chat": "Multi-Agent", "toolkits": "Toolkits", diff --git a/apps/ui/src/components/ChatSwitcher/ChatSwitcher.tsx b/apps/ui/src/components/ChatSwitcher/ChatSwitcher.tsx index 2ca658cc0..9a73d2ea1 100644 --- a/apps/ui/src/components/ChatSwitcher/ChatSwitcher.tsx +++ b/apps/ui/src/components/ChatSwitcher/ChatSwitcher.tsx @@ -197,6 +197,16 @@ const StyledRoot = styled.div<{ collapsed: boolean; theme: DefaultTheme }>` background: rgba(255, 255, 255, 0.1); } `}; + + ::-webkit-scrollbar { + display: none; + } + + /* Hide scrollbar for IE, Edge add Firefox */ + { + -ms-overflow-style: none; + scrollbar-width: none; /* Firefox */ + } ` // const StyledRoot = styled.div<{ collapsed: boolean; theme: DefaultTheme }>` // position: absolute; diff --git a/apps/ui/src/components/ChatSwitcher/useChatSwitcher.ts b/apps/ui/src/components/ChatSwitcher/useChatSwitcher.ts index ea2746463..c3502e5f4 100644 --- a/apps/ui/src/components/ChatSwitcher/useChatSwitcher.ts +++ b/apps/ui/src/components/ChatSwitcher/useChatSwitcher.ts @@ -10,7 +10,7 @@ export const useChatSwitcher = () => { useEffect(() => { const handleResize = () => { // Check the window width and update the state accordingly - setShowSwitcher(window.innerWidth >= 1000) // Adjust the breakpoint as needed + setShowSwitcher(window.innerWidth >= 900) // Adjust the breakpoint as needed } // Set the initial state on component mount diff --git a/apps/ui/src/components/ImportFile/ImportFile.tsx b/apps/ui/src/components/ImportFile/ImportFile.tsx index 5fc19ba05..719735bd9 100644 --- a/apps/ui/src/components/ImportFile/ImportFile.tsx +++ b/apps/ui/src/components/ImportFile/ImportFile.tsx @@ -1,32 +1,36 @@ import { useEffect } from 'react' import styled from 'styled-components' -import ReviewImport, { StyledButtonContainer } from './ReviewImport' +import ImportFileTable from './ImportFileTable' import useImportFile from './useImportFile' import Button from '@l3-lib/ui-core/dist/Button' +import Typography from '@l3-lib/ui-core/dist/Typography' +import MenuButton from '@l3-lib/ui-core/dist/MenuButton' + import UploadButton from 'components/UploadButton' -import { ButtonTertiary } from 'components/Button/Button' +import { ButtonPrimary, ButtonTertiary } from 'components/Button/Button' import { useDownloadTemplate } from './useDownloadTemplate' import { t } from 'i18next' +import TypographySecondary from 'components/Typography/Secondary' +import { StyledMenuButtonsWrapper } from 'pages/Agents/AgentView/components/AgentViewDetailBox' + const ImportFile = ({ setFieldValue, value = '' }: { setFieldValue: any; value?: string }) => { const { - step, parsedData, - setStep, - handleUploadJson, + setParsedData, + handleFileFormat, handleConvertJson, - handleUploadCsv, handleConvertCSVtoJSON, fileIsLoading, } = useImportFile({ setFieldValue: setFieldValue, }) - const { handleDownloadTemplate } = useDownloadTemplate() + const { handleDownloadTemplate, handleDownloadTemplateCSV } = useDownloadTemplate() useEffect(() => { if (value.length > 0) { @@ -41,9 +45,11 @@ const ImportFile = ({ setFieldValue, value = '' }: { setFieldValue: any; value?: }) .then(data => { if (fileUrl.endsWith('.json')) { - handleConvertJson(data) + const { data: convertedData } = handleConvertJson(data) + setParsedData(convertedData) } else if (fileUrl.endsWith('.csv')) { - handleConvertCSVtoJSON(data) + const { data: convertedData } = handleConvertCSVtoJSON(data) + setParsedData(convertedData) } }) .catch(error => { @@ -52,39 +58,52 @@ const ImportFile = ({ setFieldValue, value = '' }: { setFieldValue: any; value?: } }, [value]) - function renderTabs(tabIndex: number) { - switch (tabIndex) { - case 0: - return ( - - - {t('download-template')} - + return ( + <> + + + ( + + )} + closeDialogOnContentClick={false} + zIndex={2} + > + + + {t('download-json')} + + + {t('download-csv')} + + + - + {parsedData?.length === 0 && ( - - ) - - case 1: - return ( - <> - - - ) - - default: - return <>Error..! - } - } - - return ( - <> - {renderTabs(step)} + )} + {parsedData?.length > 0 && ( + { + setParsedData([]) + setFieldValue('fine_tuning_file_url', '') + }} + size={Button.sizes.SMALL} + > + {t('start-over')} + + )} + + {parsedData?.length > 0 && } + ) } @@ -94,4 +113,18 @@ export default ImportFile export const StyledFormSection = styled.div<{ columns?: string }>` width: 100%; height: 100%; + overflow: auto; + + display: flex; + flex-direction: column; + gap: 10px; +` + +const StyledButtonContainer = styled.div` + display: flex; + align-items: center; + gap: 5px; +` +const StyledMenuButton = styled(MenuButton)` + width: 140px; ` diff --git a/apps/ui/src/components/ImportFile/ImportFileTable.tsx b/apps/ui/src/components/ImportFile/ImportFileTable.tsx new file mode 100644 index 000000000..0511930f1 --- /dev/null +++ b/apps/ui/src/components/ImportFile/ImportFileTable.tsx @@ -0,0 +1,57 @@ +import React from 'react' + +import styled from 'styled-components' + +import Table from 'components/Table' + +const ImportFileTable = ({ data }: { data: any[] }) => { + const columns = [ + { + Header: 'System', + accessor: 'System', + minWidth: 75, + }, + { + Header: 'User', + accessor: 'User', + minWidth: 75, + }, + { + Header: 'Assistant', + accessor: 'Assistant', + minWidth: 75, + }, + ] + const renderTable = React.useMemo( + () => ( + <> + + + ), + [data], + ) + + return ( + <> + + {renderTable} + + + ) +} + +export default ImportFileTable + +const StyledTableWrapper = styled.div` + height: 100%; + width: 100%; +` + +const StyledContentWrapper = styled.div` + width: 100%; + overflow: auto; + + display: flex; + flex-direction: column; + gap: 10px; +` diff --git a/apps/ui/src/components/ImportFile/ReviewImport.tsx b/apps/ui/src/components/ImportFile/ReviewImport.tsx deleted file mode 100644 index f053d2acc..000000000 --- a/apps/ui/src/components/ImportFile/ReviewImport.tsx +++ /dev/null @@ -1,142 +0,0 @@ -import React from 'react' -// import { CustomTable } from 'oldComponents/atoms/CustomTable' -import useReviewImport from './useReviewImport' -import { FormikProvider } from 'formik' -import Dropdown from '@l3-lib/ui-core/dist/Dropdown' - -import styled from 'styled-components' - -import Button from '@l3-lib/ui-core/dist/Button' - -import Table from 'components/Table' -import { ButtonPrimary, ButtonTertiary } from 'components/Button/Button' -import { t } from 'i18next' -import { useDownloadTemplate } from './useDownloadTemplate' - -const ReviewImport = ({ data, setStep: startOver }: { data: any[]; setStep: any }) => { - const { handleDownloadTemplate } = useDownloadTemplate() - - const { formik, step, response, setStep } = useReviewImport(data) - - const columns = [ - { - Header: 'System', - accessor: 'System', - minWidth: 75, - }, - { - Header: 'User', - accessor: 'User', - minWidth: 75, - }, - { - Header: 'Assistant', - accessor: 'Assistant', - minWidth: 75, - }, - ] - const renderTable = React.useMemo( - () => ( - <> -
- - ), - [data], - ) - - return ( - <> - {!response ? ( - <> - - - - - {t('download-template')} - - {/* - Save - */} - startOver(0)} size={Button.sizes.SMALL}> - {t('start-over')} - - - {/* - {keys.map((item: any, index: number) => ( - - ))} - */} - {renderTable} - - - - ) : ( - <> - - - - - - - -
Total imported: {response.total_imported}
-
Total not imported: {response?.not_imported.length}
-
- - - {/* */} - - - - )} - - ) -} - -export default ReviewImport - -const StyledTableWrapper = styled.div` - height: 100%; - width: 100%; -` - -const StyledHeaderWrapper = styled.div` - width: 100%; - position: sticky; - padding: 20px 0; - top: -20px; -` -const StyledHeaderContainer = styled.div<{ itemLength?: number }>` - display: grid; - grid-auto-flow: column; - align-items: center; - grid-template-columns: ${p => p.itemLength && `repeat(${p.itemLength}, 150px)`}; - grid-column-gap: 16px; - width: 100%; - margin-right: 50px; - - background: red; -` -const StyledContentWrapper = styled.div` - width: 100%; - overflow: auto; - - display: flex; - flex-direction: column; - gap: 10px; -` - -export const StyledButtonContainer = styled.div` - display: flex; - align-items: center; - gap: 5px; -` diff --git a/apps/ui/src/components/ImportFile/useDownloadTemplate.ts b/apps/ui/src/components/ImportFile/useDownloadTemplate.ts index 38e90565a..9204883ed 100644 --- a/apps/ui/src/components/ImportFile/useDownloadTemplate.ts +++ b/apps/ui/src/components/ImportFile/useDownloadTemplate.ts @@ -25,5 +25,31 @@ export const useDownloadTemplate = () => { document.body.removeChild(a) } - return { handleDownloadTemplate } + const handleDownloadTemplateCSV = () => { + const csvRows = [] + + // Headers + const headers = Object.keys(templateData[0]) + csvRows.push(headers.join(',')) + + // Values + templateData.forEach((row: any) => { + const values = headers.map(header => { + return `"${row[header] || ''}"` + }) + csvRows.push(values.join(',')) + }) + + // Join rows + const csvContent = csvRows.join('\n') + const encodedUri = encodeURI(`data:text/csv;charset=utf-8,${csvContent}`) + + const link = document.createElement('a') + link.setAttribute('href', encodedUri) + link.setAttribute('download', 'templateData.csv') + document.body.appendChild(link) // Required for Firefox + link.click() + } + + return { handleDownloadTemplate, handleDownloadTemplateCSV } } diff --git a/apps/ui/src/components/ImportFile/useImportFile.ts b/apps/ui/src/components/ImportFile/useImportFile.ts index dabd9d298..e3640a68b 100644 --- a/apps/ui/src/components/ImportFile/useImportFile.ts +++ b/apps/ui/src/components/ImportFile/useImportFile.ts @@ -6,12 +6,45 @@ import Papa from 'papaparse' const useImportFile = ({ setFieldValue }: { setFieldValue: any }) => { const { setToast } = useContext(ToastContext) - const [step, setStep] = React.useState(0) const [fileIsLoading, setFileIsLoading] = React.useState(false) const [parsedData, setParsedData] = React.useState([]) const { uploadFile } = useUploadFile() + const validateJSON = (content: any) => { + const data = JSON.parse(content) + + if ( + Array.isArray(data) && + data.every( + obj => + typeof obj === 'object' && + 'System' in obj && + 'User' in obj && + 'Assistant' in obj && + Object.keys(obj).length === 3, + ) + ) { + return true + } else { + return false + } + } + + const validateCSV = (content: any) => { + const lines = content.split('\n') + const headers = lines[0].split(',').map((header: any) => header.trim()) + + if ( + headers.length === 3 && + headers.every((header: any) => ['System', 'User', 'Assistant'].includes(header)) + ) { + return true + } else { + return false + } + } + const handleConvertJson = (data: any) => { const dataArray = JSON.parse(data) const convertedData = dataArray.map((item: any) => ({ @@ -19,8 +52,8 @@ const useImportFile = ({ setFieldValue }: { setFieldValue: any }) => { User: item.User, Assistant: item.Assistant, })) - setParsedData(convertedData) - setStep(1) + + return { data: convertedData } } const handleConvertCSVtoJSON = (csvString: string) => { @@ -29,11 +62,11 @@ const useImportFile = ({ setFieldValue }: { setFieldValue: any }) => { skipEmptyLines: true, // Skip empty lines in CSV }) - setParsedData(data) - setStep(1) + return { data } } - const handleUploadFile = async (files: any) => { + const handleUploadFile = async (files: any, data: any) => { + setFileIsLoading(true) const promises = [] for (const file of files) { @@ -52,60 +85,60 @@ const useImportFile = ({ setFieldValue }: { setFieldValue: any }) => { const uploadedFiles = await Promise.all(promises) setFieldValue('fine_tuning_file_url', uploadedFiles?.[0].url) - } + setFileIsLoading(false) - const handleUploadJson = async (event: any) => { - const { files } = event.target - const file = files[0] - - if (file.type !== 'application/json') - return setToast({ - message: 'File must be JSON!', - type: 'negative', - open: true, - }) - - handleUploadFile(files) - - const reader = new FileReader() - - reader.onload = (event: any) => { - const data = event.target.result - - handleConvertJson(data) - } - reader.readAsText(file) + setParsedData(data) } - const handleUploadCsv = async (event: any) => { + const handleFileFormat = async (event: any) => { const { files } = event.target const file = files[0] - if (file.type !== 'text/csv') + if (file.type !== 'text/csv' && file.type !== 'application/json') return setToast({ - message: 'File must be CSV!', + message: 'File must be CSV or JSON format!', type: 'negative', open: true, }) - handleUploadFile(files) - const reader = new FileReader() - - reader.onload = (event: any) => { - const csvString = event.target.result - handleConvertCSVtoJSON(csvString) + reader.onload = async (event: any) => { + const fileData = event.target.result + + if (file.type === 'text/csv') { + const isValid = validateCSV(fileData) + if (isValid) { + const { data } = handleConvertCSVtoJSON(fileData) + await handleUploadFile(files, data) + } else { + return setToast({ + message: 'Data Fields are incorrect!', + type: 'negative', + open: true, + }) + } + } else if (file.type === 'application/json') { + const isValid = validateJSON(fileData) + if (isValid) { + const { data } = handleConvertJson(fileData) + await handleUploadFile(files, data) + } else { + return setToast({ + message: 'Data Fields are incorrect!', + type: 'negative', + open: true, + }) + } + } } reader.readAsText(file) } return { - handleUploadCsv, - handleUploadJson, - step, + handleFileFormat, parsedData, - setStep, + setParsedData, handleConvertJson, handleConvertCSVtoJSON, fileIsLoading, diff --git a/apps/ui/src/components/ImportFile/useReviewImport.ts b/apps/ui/src/components/ImportFile/useReviewImport.ts deleted file mode 100644 index 817f7d666..000000000 --- a/apps/ui/src/components/ImportFile/useReviewImport.ts +++ /dev/null @@ -1,191 +0,0 @@ -import React from 'react' -import { useFormik } from 'formik' -import * as yup from 'yup' -import { useGetDownloadUrl } from 'services' - -import useSnackbarAlert from 'hooks/useSnackbar' - -import { useTranslation } from 'react-i18next' - -const field_names = [ - { label: 'System', value: 'system' }, - { label: 'User', value: 'user' }, - { label: 'Assistant', value: 'assistant' }, -] - -const csv_keys = ['System', 'User', 'Assistant'] - -const generateValidationSchema = (keys: string[]) => { - const obj: any = {} - // eslint-disable-next-line array-callback-return - keys.map(item => { - obj[item] = yup.string().required(`Required!`) - }) - - return yup.object().shape({ - ...obj, - }) -} - -const useReviewImport = (data: any) => { - const { t } = useTranslation() - const { setSnackbar } = useSnackbarAlert() - - const [keys, setKeys] = React.useState([]) - const [custom_field_keys, setCustomFieldKeys] = React.useState([]) - const [validationSchema, setValidationSchema] = React.useState(null) - const [step, setStep] = React.useState(0) - const [response, setResponse] = React.useState(null) - const [file_options, setFileOptions] = React.useState([]) - - const { data: template } = useGetDownloadUrl('template/Template_asset.csv') - - // console.log('template:;', template) - - const formik = useFormik({ - initialValues: {}, - // validationSchema: validationSchema, - enableReinitialize: true, - onSubmit: values => handleSubmit(values), - }) - - React.useEffect(() => { - if (data && data.length) { - const object: any = {} - let keys_from_csv: string[] = [] - let custom_keys: any = [] - let f_options: any = [] - - // eslint-disable-next-line array-callback-return - Object.keys(data[0]).map((key, index) => { - if (key === csv_keys[index]) { - object[field_names[index].value] = field_names[index].value - keys_from_csv = [...keys_from_csv, field_names[index].value] - f_options = [...f_options, { key: field_names[index].value, label: key }] - } else { - keys_from_csv = [...keys_from_csv, key.toLowerCase().replaceAll(' ', '_')] - object[key.toLowerCase().replaceAll(' ', '_')] = '' - custom_keys = [ - ...custom_keys, - { - label: key, - value: key.toLowerCase().replaceAll(' ', '_'), - }, - ] - - f_options = [ - ...f_options, - { key: key.toLowerCase().replaceAll(' ', '_'), label: key, is_custom_field: true }, - ] - } - }) - - const validationSchema = generateValidationSchema(keys_from_csv) - - formik.setValues(object) - setKeys(keys_from_csv) - setCustomFieldKeys(custom_keys) - setValidationSchema(validationSchema) - setFileOptions(f_options) - } - }, [data]) - - const handleSubmit = async function (values: any) { - try { - const new_array = data.map((item: any) => { - const obj: any = { custom_props: [] } - - // eslint-disable-next-line array-callback-return - keys.map((key: any) => { - const option: any = field_names.find(i => i.value === key) - - obj[key] = option?.label ? item[option.label] : null - - // if (key === 'price') { - // obj.price = parseFloat(item[option.label]) - // } - - // if (key === 'token_id') { - // obj.token_id = parseInt(item[option.label]) - // } - - // if (key === 'properties' && item[option.label]) { - // obj.properties = item[option.label].split(',') - // } - - // if (key === 'number_of_copies') { - // obj.supply = parseInt(item[option.label]) - // delete obj.number_of_copies - // } - }) - - // for (const key in values) { - // if (values[key] === 'custom_field') { - // const cf = custom_field_keys.find((i: any) => i.value === key) - // obj.custom_props = [...obj.custom_props, { [key]: item[cf.label] }] - // } - - // if (values[key] === 'custom_field' || values[key] === 'no_import') { - // delete obj[key] - // } - // } - - return obj - }) - // if (!collection) return - // const gameId = collection?.game_id || '' - // const collectionId = collection?.id || '' - // const result = await insertAssetsService( - // { input: new_array, file_options }, - // gameId, - // collectionId, - // ) - - // if (result.success) { - // setSnackbar({ - // message: t('import-was-successfully'), - // variant: 'success', - // }) - // setResponse(result) - // } - } catch (error) { - setSnackbar({ message: t('failed-to-import'), variant: 'error' }) - } - } - - // const { config } = columnConfig({ keys: Object.keys(data[0]) }) - - const options = field_names.map(i => ({ - ...i, - ...(Object.values(formik.values) - .filter(n => n !== 'custom_field' && n !== 'no_import') - .includes(i.value) - ? { isDisabled: true } - : {}), - })) - - React.useEffect(() => { - if (!formik.isSubmitting) return - if (Object.keys(formik.errors).length > 0) { - setSnackbar({ message: t('please-fill-out-required-fields!'), variant: 'error' }) - // const error = document.getElementsByName(Object.keys(formik.errors)[0])[0] - // if (error) { - // error.scrollIntoView() - // console.log(error) - // } - } - }, [formik]) - - return { - // columnConfig: config, - formik, - keys, - options: options, - csv_keys, - step, - response, - setStep, - } -} - -export default useReviewImport diff --git a/apps/ui/src/i18n/locales/en.json b/apps/ui/src/i18n/locales/en.json index be37abf5c..106352541 100644 --- a/apps/ui/src/i18n/locales/en.json +++ b/apps/ui/src/i18n/locales/en.json @@ -14,6 +14,8 @@ "add-agent": "Add $t(agent)", "add-team": "Add $t(team)", "add-datasource": "Add $t(datasource)", + "add-contact": "Add $t(contact)", + "add-group": "Add $t(group)", "add-member": "Add member", "add-fine-tuning": "Add Fine-Tuning", "advanced-options": "Advanced Options", @@ -54,6 +56,7 @@ "brand": "Brand", "beta": "Beta", "cancel": "Cancel", + "contact": "Contact", "cannot-update-password": "Cannot update password", "chargers": "Chargers", "chat": "Chat", @@ -112,6 +115,8 @@ "done": "Done", "dont-have-account": "Don't have an account?", "download-template": "Download Template", + "download-json": "JSON", + "download-csv": "CSV", "edit-agent": "Edit $t(agent)", "edit-api-keys": "Edit API keys", "edit-team": "Edit $t(team)", @@ -144,6 +149,7 @@ "game-successfully-deleted": "Game successfully deleted", "game-delete-failed": "Game delete failed", "github": "Github", + "group": "Group", "goals": "Goal", "goal": "1 Goal", "greeting": "Greeting", @@ -287,6 +293,7 @@ "update": "Update", "upload-csv": "Upload CSV", "upload-json": "Upload JSON", + "upload-file": "Upload file", "update-password": "Update password", "user-successfully-deleted": "User successfully deleted", "user-delete-failed": "User delete failed", diff --git a/apps/ui/src/modals/AIChatModal/components/ChatMembers/ChatMembers.tsx b/apps/ui/src/modals/AIChatModal/components/ChatMembers/ChatMembers.tsx index d181c60b9..a79a9f1b1 100644 --- a/apps/ui/src/modals/AIChatModal/components/ChatMembers/ChatMembers.tsx +++ b/apps/ui/src/modals/AIChatModal/components/ChatMembers/ChatMembers.tsx @@ -22,6 +22,7 @@ import { StyledEditIcon, StyledEyeOpenIcon, } from 'pages/TeamOfAgents/TeamOfAgentsCard/TeamOfAgentsCard' +import { StyledTabListSpan, StyledTabListWrapper } from 'styles/tabStyles.css' const ChatMembers = ({ agentById, @@ -50,16 +51,16 @@ const ChatMembers = ({ return ( - + setActiveTab(0)}> - {t('info')} + {t('info')} setActiveTab(1)}> - {t('members')} + {t('members')} - + @@ -128,16 +129,16 @@ const ChatMembers = ({ if (teamOfAgents) { return ( - + setActiveTab(0)}> - {t('info')} + {t('info')} setActiveTab(1)}> - {t('members')} + {t('members')} - + @@ -260,10 +261,3 @@ const StyledIconButtonWrapper = styled.div` display: flex; align-items: center; ` -const StyledDiv = styled.div` - border: ${({ theme }) => theme.body.secondaryBorder}; - border-radius: 20px; -` -const StyledSpan = styled.span` - color: ${({ theme }) => theme.body.textColorPrimary}; -` diff --git a/apps/ui/src/pages/Agents/AgentView/components/AgentViewDetailBox.tsx b/apps/ui/src/pages/Agents/AgentView/components/AgentViewDetailBox.tsx index 1fa52a0db..e85efee2e 100644 --- a/apps/ui/src/pages/Agents/AgentView/components/AgentViewDetailBox.tsx +++ b/apps/ui/src/pages/Agents/AgentView/components/AgentViewDetailBox.tsx @@ -25,6 +25,7 @@ import MenuButton from '@l3-lib/ui-core/dist/MenuButton' import MenuDots from '@l3-lib/ui-core/dist/icons/MenuDots' import { useAgents } from 'pages/Agents/useAgents' import { useGetAccountModule } from 'utils/useGetAccountModule' +import { useModelsService } from 'services' type AgentViewDetailBoxProps = { agentData: any @@ -42,15 +43,23 @@ const AgentVIewDetailBox = ({ agentData }: AgentViewDetailBoxProps) => { const { deleteAgentHandler } = useAgents() const navigate = useNavigate() + const { closeModal, openModal } = useModal() + const { data: models } = useModelsService() + const { agent, configs } = agentData + const { name, description, role, creator, is_template } = agent - const { model_version, model_provider, temperature } = configs + const { model, temperature } = configs const isCreator = user?.id === agent?.created_by + const agentModel = models + ?.filter((modelData: any) => modelData.id === model) + .map((model: any) => model.name) + const handleEdit = () => { closeModal('agent-view-modal') navigate(`/agents/${agent?.id}/edit-agent`) @@ -65,7 +74,7 @@ const AgentVIewDetailBox = ({ agentData }: AgentViewDetailBoxProps) => { openModal({ name: 'schedule-run-modal', data: { id: chatId || agent.id, type: 'agent' } }) } - + console.log(model) return ( @@ -147,9 +156,7 @@ const AgentVIewDetailBox = ({ agentData }: AgentViewDetailBoxProps) => { {role && } - {model_provider && } - - {model_version && } + {agentModel?.length > 0 && } {temperature && } @@ -209,7 +216,7 @@ export const StyledMenuButtonsWrapper = styled.div` backdrop-filter: blur(100px); padding: 10px; border-radius: 10px; - width: 200px; + min-width: fit-content; display: flex; diff --git a/apps/ui/src/pages/Agents/Agents.tsx b/apps/ui/src/pages/Agents/Agents.tsx index 75313e374..bf33fcb58 100644 --- a/apps/ui/src/pages/Agents/Agents.tsx +++ b/apps/ui/src/pages/Agents/Agents.tsx @@ -100,12 +100,12 @@ export const StyledCardsWrapper = styled.div` flex-wrap: wrap; gap: 16px; - max-width: 1055px; + /* max-width: 1055px; */ width: 100%; max-height: calc(100vh - 325px); height: 100%; overflow-y: auto; - padding: 5px; + padding: 5px 35px; ` const StyledMainHeaderWrapper = styled.div` display: flex; diff --git a/apps/ui/src/pages/Models/FineTuning/FineTunings.tsx b/apps/ui/src/pages/Models/FineTuning/FineTunings.tsx index 4f6a11a03..f0a46bf67 100644 --- a/apps/ui/src/pages/Models/FineTuning/FineTunings.tsx +++ b/apps/ui/src/pages/Models/FineTuning/FineTunings.tsx @@ -5,7 +5,6 @@ import ComponentsWrapper from 'components/ComponentsWrapper/ComponentsWrapper' import { useTranslation } from 'react-i18next' import { StyledHeaderGroup, - StyledSectionDescription, StyledSectionTitle, StyledSectionWrapper, } from 'pages/Home/homeStyle.css' @@ -17,7 +16,6 @@ import IconButton from '@l3-lib/ui-core/dist/IconButton' import { useNavigate } from 'react-router-dom' -import { StyledCardsWrapper } from 'pages/Agents/Agents' import { useFineTuning } from './useFineTuning' import { useFineTuningForm } from './FineTuningForm/useFineTuningForm' @@ -26,6 +24,7 @@ import { StyledDeleteIcon, StyledEditIcon, } from 'pages/TeamOfAgents/TeamOfAgentsCard/TeamOfAgentsCard' +import { StyledTableWrapper } from 'plugins/contact/pages/Contact/Contacts' const FineTunings = () => { const { t } = useTranslation() @@ -44,21 +43,23 @@ const FineTunings = () => { { Header: 'Name', accessor: 'name', + width: 500, }, { Header: 'Model', accessor: 'model', + width: 370, }, { Header: 'Status', accessor: 'status', - width: 250, + width: 100, }, { Header: 'Actions', accessor: 'id', - width: 250, + width: 100, Cell: ({ cell }: any) => { return ( @@ -90,8 +91,10 @@ const FineTunings = () => { id: fineTuning.id, name: fineTuning.name, status: fineTuning.status, - model: modelOptions?.filter((model: any) => model.value === fineTuning?.model_id).length > 0 ? - modelOptions?.filter((model: any) => model.value === fineTuning?.model_id)?.[0].label: '', + model: + modelOptions?.filter((model: any) => model.value === fineTuning?.model_id).length > 0 + ? modelOptions?.filter((model: any) => model.value === fineTuning?.model_id)?.[0].label + : '', })) || [] return ( @@ -107,9 +110,9 @@ const FineTunings = () => { - +
- + ) diff --git a/apps/ui/src/pages/Models/Models.tsx b/apps/ui/src/pages/Models/Models.tsx index 78269a116..ef7a31c2f 100644 --- a/apps/ui/src/pages/Models/Models.tsx +++ b/apps/ui/src/pages/Models/Models.tsx @@ -15,48 +15,78 @@ import { StyledCardsWrapper } from 'pages/Agents/Agents' import FineTunings from './FineTuning/FineTunings' -import { StyledRoot } from 'pages/Discover/Discover' +import Tab from '@l3-lib/ui-core/dist/Tab' +import TabList from '@l3-lib/ui-core/dist/TabList' +import TabPanel from '@l3-lib/ui-core/dist/TabPanel' +import TabPanels from '@l3-lib/ui-core/dist/TabPanels' +import TabsContext from '@l3-lib/ui-core/dist/TabsContext' + +import { useState } from 'react' +import { StyledTabListSpan, StyledTabListWrapper, StyledTabRootWrapper } from 'styles/tabStyles.css' const Models = ({ isPublic }: { isPublic?: boolean }) => { const { t } = useTranslation() + const [activeTab, setActiveTab] = useState(0) + const { data: models } = useModelsService() return ( - - - - - -
- {`${t('model')}`} - {t('model-description')} -
-
- - - - {models - ?.filter(model => !model.is_fine_tuned) - ?.map((model, index: number) => { - const logo = MODEL_PROVIDER_LOGOS.find(logo => logo.provider === model.provider) - const logoSrc = logo?.logoSrc || '' - - return ( - - ) - })} - - -
-
+ + + + setActiveTab(0)}> + {t('fine-tuning')} + + setActiveTab(1)}> + {`${t('model')}s`} + + + + + + + + + + + + + +
+ {`${t('model')}s`} + {t('model-description')} +
+
+ + + + {models + ?.filter(model => !model.is_fine_tuned) + ?.map((model, index: number) => { + const logo = MODEL_PROVIDER_LOGOS.find( + logo => logo.provider === model.provider, + ) + const logoSrc = logo?.logoSrc || '' + + return ( + + ) + })} + + +
+
+
+
+
) } diff --git a/apps/ui/src/pages/Navigation/MainNavigation.tsx b/apps/ui/src/pages/Navigation/MainNavigation.tsx index 4498bfb2c..47ff0b10f 100644 --- a/apps/ui/src/pages/Navigation/MainNavigation.tsx +++ b/apps/ui/src/pages/Navigation/MainNavigation.tsx @@ -100,7 +100,6 @@ const MainNavigation = () => { Agents */} - {isContact && ( { > )} - {isGroup && ( + {/* {isGroup && ( onHandleClick('/groups')}> - )} + )} */} {isDatasource && ( { onHandleClick('/models')}> @@ -179,7 +178,6 @@ const MainNavigation = () => { )} - {isDiscover && ( { const navigate = useNavigate() @@ -58,10 +66,12 @@ const Contacts = () => { { Header: 'Name', accessor: 'name', + width: 225, }, { Header: 'Phone', accessor: 'phone', + width: 200, Cell: ({ cell }: any) => { return ( @@ -109,11 +119,12 @@ const Contacts = () => { { Header: 'Email', accessor: 'email', + width: 200, }, { Header: 'Group', accessor: 'group_id', - + width: 100, Cell: ({ cell }: any) => { return ( @@ -127,11 +138,12 @@ const Contacts = () => { { Header: 'Description', accessor: 'description', + width: 250, }, { Header: 'Actions', accessor: 'id', - maxWidth: 100, + width: 100, Cell: ({ cell }: any) => { return ( @@ -158,28 +170,53 @@ const Contacts = () => { [], ) + const [activeTab, setActiveTab] = useState(0) + return ( - - -
- Contacts - {/* - Here is your datasource, a collection of databases, APIs, files, and more. - */} -
-
- navigate('/contacts/create-contact')} size={'small'}> - Add Contact - -
-
- - - -
- - - + + + + setActiveTab(0)}> + {`${t('contact')}s`} + + setActiveTab(1)}> + {`${t('group')}s`} + + + + + + + + + +
+ {`${t('contact')}s`} +
+
+ navigate('/contacts/create-contact')} + size={'small'} + > + {t('add-contact')} + +
+
+ + + +
+ + + + + + + + + + + ) } @@ -223,4 +260,6 @@ export const StyledTableWrapper = styled.div` width: 100%; height: 100%; padding: 0 15px; + overflow: auto; + max-height: calc(100vh - 325px); ` diff --git a/apps/ui/src/plugins/contact/pages/Group/Groups.tsx b/apps/ui/src/plugins/contact/pages/Group/Groups.tsx index 6bcc5a0a4..92337825d 100644 --- a/apps/ui/src/plugins/contact/pages/Group/Groups.tsx +++ b/apps/ui/src/plugins/contact/pages/Group/Groups.tsx @@ -21,6 +21,7 @@ import { import styled from 'styled-components' import Table from 'components/Table' import { StyledTableWrapper } from '../Contact/Contacts' +import { t } from 'i18next' const Groups = () => { const { groups, deleteGroupHandler } = useGroups() @@ -39,17 +40,17 @@ const Groups = () => { { Header: 'Name', accessor: 'name', - width: 400, + width: 485, }, { Header: 'Description', accessor: 'description', - width: 400, + width: 485, }, { Header: 'Actions', accessor: 'id', - width: 300, + width: 100, Cell: ({ cell }: any) => { return ( @@ -80,14 +81,14 @@ const Groups = () => {
- Groups + {`${t('group')}s`} {/* Here is your datasource, a collection of databases, APIs, files, and more. */}
navigate('/groups/create-group')} size={'small'}> - Add Group + {t('add-group')}
@@ -106,4 +107,5 @@ export default Groups export const StyledTableButtons = styled.div` display: flex; align-items: center; + height: 100%; ` diff --git a/apps/ui/src/routes/ChatRouteLayout.tsx b/apps/ui/src/routes/ChatRouteLayout.tsx index 4f39520f2..97413e89f 100644 --- a/apps/ui/src/routes/ChatRouteLayout.tsx +++ b/apps/ui/src/routes/ChatRouteLayout.tsx @@ -42,6 +42,8 @@ const ChatRouteLayout = () => { const [showChats, setShowChats] = useState(true) const [showInfo, setShowInfo] = useState(false) + const [isSmallScreen, setIsSmallScreen] = useState(false) + const outlet = useOutlet() const { agentsData, deleteAgentHandler, agentsLoading } = useAgents() const { @@ -100,9 +102,25 @@ const ChatRouteLayout = () => { } }, [expand]) - if (!user && !chatId) return + useEffect(() => { + // Function to update the 'show' state based on screen width + const handleResize = () => { + setIsSmallScreen(window.innerWidth < 1600 && window.innerWidth > 1050) + } + + // Initial setup + handleResize() + + // Listen for window resize events + window.addEventListener('resize', handleResize) + + // Clean up the event listener when the component unmounts + return () => { + window.removeEventListener('resize', handleResize) + } + }, []) - const hasChat = !!chatId + if (!user && !chatId) return return ( @@ -116,6 +134,7 @@ const ChatRouteLayout = () => { )} {expand && !showInfo && location.pathname.includes('/chat') && ( setShowInfo(true)} onMouseEnter={() => setShowInfo(true)} @@ -133,8 +152,8 @@ const ChatRouteLayout = () => { {user && ( {teamModule?.list && ( <> @@ -310,7 +329,10 @@ const StyledContainer = styled.div` position: relative; ` -const StyledLeftColumn = styled.div<{ right?: boolean; isHidden?: boolean; hasChat: boolean }>` +const StyledLeftColumn = styled.div<{ + isHidden?: boolean + isSmallScreen: boolean +}>` /* background: ${({ theme }) => theme.body.cardBgColor}; */ border-right: ${({ theme }) => location.pathname.includes('/chat') ? theme.body.secondaryBorder : 'none'}; @@ -332,7 +354,7 @@ const StyledLeftColumn = styled.div<{ right?: boolean; isHidden?: boolean; hasCh padding-left: 120px; height: 100%; - min-width: 450px; + min-width: 475px; max-height: calc(100vh - 185px); @@ -345,8 +367,16 @@ const StyledLeftColumn = styled.div<{ right?: boolean; isHidden?: boolean; hasCh overflow: hidden; cursor: pointer; `} + ${props => + props.isSmallScreen && + css` + margin-left: 0; + overflow: auto; + cursor: pointer; + position: static; + `} ` -const StyledRightColumn = styled.div<{ isHidden?: boolean }>` +const StyledRightColumn = styled.div<{ isHidden: boolean }>` position: absolute; right: 0; z-index: 10000; @@ -399,7 +429,7 @@ const StyledOutletWrapper = styled.div` margin-left: 450px; max-width: 1500px; ` -const StyledShowButton = styled.div<{ isRight?: boolean }>` +const StyledShowButton = styled.div<{ isRight?: boolean; isSmallScreen?: boolean }>` height: 100vh; width: calc(30% - 120px); @@ -415,6 +445,11 @@ const StyledShowButton = styled.div<{ isRight?: boolean }>` right: 0; margin-left: auto; `} + ${props => + props.isSmallScreen && + css` + width: calc(15% - 150px); + `} ` const StyledMiddleArea = styled.div` height: 100%; diff --git a/apps/ui/src/styles/tabStyles.css.ts b/apps/ui/src/styles/tabStyles.css.ts new file mode 100644 index 000000000..339d194b8 --- /dev/null +++ b/apps/ui/src/styles/tabStyles.css.ts @@ -0,0 +1,20 @@ +import styled from 'styled-components' + +export const StyledTabRootWrapper = styled.div` + display: flex; + flex-direction: column; + + width: 100%; + + gap: 20px; +` + +export const StyledTabListWrapper = styled.div` + border: ${({ theme }) => theme.body.secondaryBorder}; + border-radius: 30px; + width: fit-content; + margin: 0 auto; +` +export const StyledTabListSpan = styled.span` + color: ${({ theme }) => theme.body.textColorPrimary}; +`