From bcabd244b759ca63b69906d2ecd4b0ccce74db7c Mon Sep 17 00:00:00 2001 From: akanshaaaa19 Date: Tue, 23 Apr 2024 18:16:51 +0530 Subject: [PATCH 01/14] added menu and basic layout for knowledge base --- .../icons/SideDrawer/KnowledgeBaseIcon.tsx | 15 ++++ src/assets/images/icons/UploadIcon.svg | 8 ++ src/components/UI/ListIcon/ListIcon.tsx | 2 + .../UI/UploadFile/UploadFile.module.css | 10 +++ src/components/UI/UploadFile/UploadFile.tsx | 35 +++++++++ src/config/menu.ts | 7 ++ .../KnowledgeBase/KnowledgeBase.tsx | 73 +++++++++++++++++++ .../KnowledgeBase/Knowledgebase.module.css | 0 src/graphql/queries/KnowledgeBase.ts | 21 ++++++ .../AuthenticatedRoute/AuthenticatedRoute.tsx | 3 + 10 files changed, 174 insertions(+) create mode 100644 src/assets/images/icons/SideDrawer/KnowledgeBaseIcon.tsx create mode 100644 src/assets/images/icons/UploadIcon.svg create mode 100644 src/components/UI/UploadFile/UploadFile.module.css create mode 100644 src/components/UI/UploadFile/UploadFile.tsx create mode 100644 src/containers/KnowledgeBase/KnowledgeBase.tsx create mode 100644 src/containers/KnowledgeBase/Knowledgebase.module.css create mode 100644 src/graphql/queries/KnowledgeBase.ts diff --git a/src/assets/images/icons/SideDrawer/KnowledgeBaseIcon.tsx b/src/assets/images/icons/SideDrawer/KnowledgeBaseIcon.tsx new file mode 100644 index 000000000..252d8f93a --- /dev/null +++ b/src/assets/images/icons/SideDrawer/KnowledgeBaseIcon.tsx @@ -0,0 +1,15 @@ +export const KnowledgeBaseIcon = ({ color }: any) => { + return ( + + {'Group'} + + + + + + + + ); +}; + +export default KnowledgeBaseIcon; diff --git a/src/assets/images/icons/UploadIcon.svg b/src/assets/images/icons/UploadIcon.svg new file mode 100644 index 000000000..dbf57503f --- /dev/null +++ b/src/assets/images/icons/UploadIcon.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/components/UI/ListIcon/ListIcon.tsx b/src/components/UI/ListIcon/ListIcon.tsx index 3423c7b9f..7cfc3af0d 100644 --- a/src/components/UI/ListIcon/ListIcon.tsx +++ b/src/components/UI/ListIcon/ListIcon.tsx @@ -27,6 +27,7 @@ import ConsultingIcon from 'assets/images/icons/SideDrawer/ConsultingIcon'; import WaChatIcon from 'assets/images/icons/SideDrawer/WaGroupChat'; import WaCollectionIcon from 'assets/images/icons/SideDrawer/WaGroupCollection'; import WaGroupIcon from 'assets/images/icons/SideDrawer/WhatsAppGroupIcon'; +import KnowledgeBaseIcon from 'assets/images/icons/SideDrawer/KnowledgeBaseIcon'; import styles from './ListIcon.module.css'; import FiberNewIcon from '@mui/icons-material/FiberNew'; import { Badge } from '@mui/material'; @@ -71,6 +72,7 @@ export const ListIcon = ({ icon = '', selected = false, count }: ListIconProps) waGroupCollection: WaCollectionIcon, waGroupChat: WaChatIcon, waGroup: WaGroupIcon, + knowledgeBase: KnowledgeBaseIcon, }; const iconImage = stringsToIcons[icon] && ( diff --git a/src/components/UI/UploadFile/UploadFile.module.css b/src/components/UI/UploadFile/UploadFile.module.css new file mode 100644 index 000000000..8bcb69b19 --- /dev/null +++ b/src/components/UI/UploadFile/UploadFile.module.css @@ -0,0 +1,10 @@ +.Container { + padding: 1rem 0; + display: flex; + align-items: center; + justify-content: center; + column-gap: 1rem; + border: 1px solid #119656; + border-style: dashed; + width: 100%; +} diff --git a/src/components/UI/UploadFile/UploadFile.tsx b/src/components/UI/UploadFile/UploadFile.tsx new file mode 100644 index 000000000..2822a40bc --- /dev/null +++ b/src/components/UI/UploadFile/UploadFile.tsx @@ -0,0 +1,35 @@ +import { styled } from '@mui/material/styles'; +import Button from '@mui/material/Button'; +import UploadIcon from 'assets/images/icons/UploadIcon.svg?react'; +import styles from './UploadFile.module.css'; + +export const UploadFile = () => { + const VisuallyHiddenInput = styled('input')({ + clip: 'rect(0 0 0 0)', + clipPath: 'inset(50%)', + height: 1, + overflow: 'hidden', + position: 'absolute', + bottom: 0, + left: 0, + whiteSpace: 'nowrap', + width: 1, + }); + + return ( + + ); +}; diff --git a/src/config/menu.ts b/src/config/menu.ts index 7692e316e..b0a1562ce 100644 --- a/src/config/menu.ts +++ b/src/config/menu.ts @@ -221,6 +221,13 @@ const menus = (): Menu[] => [ icon: 'consulting', roles: ['Glific_admin'], }, + { + title: 'Knowledge Base', + path: '/knowledge-base', + type: 'sideDrawer', + icon: 'knowledgeBase', + roles: allRoles, + }, ], }, diff --git a/src/containers/KnowledgeBase/KnowledgeBase.tsx b/src/containers/KnowledgeBase/KnowledgeBase.tsx new file mode 100644 index 000000000..abacb06a2 --- /dev/null +++ b/src/containers/KnowledgeBase/KnowledgeBase.tsx @@ -0,0 +1,73 @@ +import { List } from 'containers/List/List'; +import { useTranslation } from 'react-i18next'; +import CollectionIcon from 'assets/images/icons/Collection/Dark.svg?react'; +import { GET_COLLECTIONS_COUNT, FILTER_COLLECTIONS } from 'graphql/queries/Collection'; +import styles from './Knowledgebase.module.css'; +import { DELETE_COLLECTION } from 'graphql/mutations/Collection'; +import { useState } from 'react'; +import { DialogBox } from 'components/UI/DialogBox/DialogBox'; +import { UploadFile } from 'components/UI/UploadFile/UploadFile'; + +export const KnowledgeBase = () => { + const queries = { + countQuery: GET_COLLECTIONS_COUNT, + filterItemsQuery: FILTER_COLLECTIONS, + deleteItemQuery: DELETE_COLLECTION, + }; + + const [dialogOpen, setDialogOpen] = useState(false); + + const { t } = useTranslation(); + const getLabel = (label: string) =>
{label}
; + const getId = (id: string) =>
{id}
; + + const getColumns = ({ label, contactsCount, description, waGroupsCount, id }: any) => { + return { + label: getLabel(label), + description: getId(id), + }; + }; + + const columnAttributes = { + columns: getColumns, + columnStyles: [], + }; + const collectionIcon = ; + + const dialog = ( + setDialogOpen(false)} + buttonOk={t('Upload')} + alignButtons="center" + > +
+ +
+
+ ); + + return ( + <> + setDialogOpen(true) }} + filters={{}} + pageLink={`knowledge-base`} + listIcon={collectionIcon} + {...queries} + {...columnAttributes} + /> + {dialogOpen && dialog} + + ); +}; + +export default KnowledgeBase; diff --git a/src/containers/KnowledgeBase/Knowledgebase.module.css b/src/containers/KnowledgeBase/Knowledgebase.module.css new file mode 100644 index 000000000..e69de29bb diff --git a/src/graphql/queries/KnowledgeBase.ts b/src/graphql/queries/KnowledgeBase.ts new file mode 100644 index 000000000..7d256f220 --- /dev/null +++ b/src/graphql/queries/KnowledgeBase.ts @@ -0,0 +1,21 @@ +import { gql } from '@apollo/client'; + +export const GET_DOCUMENTS = gql` + query getGroup($id: ID!) { + group(id: $id) { + group { + id + label + roles { + id + label + } + description + users { + id + name + } + } + } + } +`; diff --git a/src/routes/AuthenticatedRoute/AuthenticatedRoute.tsx b/src/routes/AuthenticatedRoute/AuthenticatedRoute.tsx index 54f96884a..815a89a6a 100644 --- a/src/routes/AuthenticatedRoute/AuthenticatedRoute.tsx +++ b/src/routes/AuthenticatedRoute/AuthenticatedRoute.tsx @@ -72,6 +72,7 @@ const InteractiveMessage = lazy(() => import('containers/InteractiveMessage/Inte const RoleList = lazy(() => import('containers/Role/RoleList/RoleList')); const Role = lazy(() => import('containers/Role/Role')); +const KnowledgeBase = lazy(() => import('containers/KnowledgeBase/KnowledgeBase')); const routeStaff = ( @@ -152,6 +153,8 @@ const routeAdmin = ( } /> } /> + } /> + } /> ); From 5c5e96f5ba85179aeaa3a0c96d496f422a0aee6c Mon Sep 17 00:00:00 2001 From: akanshaaaa19 Date: Wed, 24 Apr 2024 22:15:21 +0530 Subject: [PATCH 02/14] connected apis --- src/components/UI/Pager/Pager.module.css | 14 ++- src/components/UI/Pager/Pager.tsx | 16 +++- .../UI/UploadFile/UploadFile.module.css | 41 +++++++- src/components/UI/UploadFile/UploadFile.tsx | 95 ++++++++++++++----- .../KnowledgeBase/KnowledgeBase.tsx | 68 ++++++++++--- .../KnowledgeBase/Knowledgebase.module.css | 32 +++++++ src/containers/List/List.tsx | 33 ++++--- src/graphql/mutations/KnowledgeBase.ts | 9 ++ src/graphql/queries/KnowledgeBase.ts | 22 ++--- 9 files changed, 257 insertions(+), 73 deletions(-) create mode 100644 src/graphql/mutations/KnowledgeBase.ts diff --git a/src/components/UI/Pager/Pager.module.css b/src/components/UI/Pager/Pager.module.css index b04b9b967..f2340bd2a 100644 --- a/src/components/UI/Pager/Pager.module.css +++ b/src/components/UI/Pager/Pager.module.css @@ -80,6 +80,10 @@ align-items: center; } +.BodyFull { + height: 100%; +} + .RowStyle { padding: 15px 20px 15px 25px; } @@ -97,13 +101,21 @@ } .StyleForContainer { - height: calc(100% - 50px); background: #fff; overflow-y: scroll; scrollbar-width: none; border-radius: 10px 10px 0 0; } +.HeightFull { + height: 100%; + border-radius: 10px; +} + +.HeightShort { + height: calc(100% - 50px); +} + .Skeleton { margin: 10px; } diff --git a/src/components/UI/Pager/Pager.tsx b/src/components/UI/Pager/Pager.tsx index 12cb7e9fb..23e6ea217 100644 --- a/src/components/UI/Pager/Pager.tsx +++ b/src/components/UI/Pager/Pager.tsx @@ -30,6 +30,7 @@ interface PagerProps { loadingList?: boolean; collapseRow: string | undefined; noItemsText?: any; + showPagination?: boolean; } // TODO: cleanup the translations code @@ -191,6 +192,7 @@ export const Pager = ({ collapseRow, loadingList = false, noItemsText, + showPagination = true, }: PagerProps) => { const rows = createRows(data, columnStyles, collapseRow, collapseOpen); const tableHead = tableHeadColumns(columnNames, columnStyles, tableVals, handleTableChange); @@ -198,7 +200,9 @@ export const Pager = ({ return (
- + {tableHead}{!loadingList && data.length > 0 && rows} @@ -216,11 +220,13 @@ export const Pager = ({ ))} )} - {!loadingList && data.length == 0 &&
{noItemsText}
} + {!loadingList && data.length == 0 &&
{noItemsText}
} -
-
{tablePagination}
-
+ {showPagination && ( +
+
{tablePagination}
+
+ )} ); }; diff --git a/src/components/UI/UploadFile/UploadFile.module.css b/src/components/UI/UploadFile/UploadFile.module.css index 8bcb69b19..7ab7c94fd 100644 --- a/src/components/UI/UploadFile/UploadFile.module.css +++ b/src/components/UI/UploadFile/UploadFile.module.css @@ -1,10 +1,47 @@ .Container { padding: 1rem 0; display: flex; - align-items: center; + align-items: flex-start; justify-content: center; - column-gap: 1rem; border: 1px solid #119656; border-style: dashed; width: 100%; } +.FormError { + color: #f44336; + font-size: 0.75rem; + text-align: left; + font-weight: 400; + margin: 0; +} + +.Container input { + display: none; +} + +.Container span { + margin-bottom: 8px; +} + +.Container div { + padding-top: 4px; + display: flex; + align-items: flex-start; + justify-content: center; + column-gap: 0.8rem; +} + +.HelperText { + margin: 0px; + padding: 0px; + color: #93a29b; + line-height: 1; + font-size: 12px; + margin-top: 7px; + padding: 0 8px; +} + +.WaitUpload { + font-size: 14px; + margin-left: 16px; +} diff --git a/src/components/UI/UploadFile/UploadFile.tsx b/src/components/UI/UploadFile/UploadFile.tsx index 2822a40bc..3e35480e8 100644 --- a/src/components/UI/UploadFile/UploadFile.tsx +++ b/src/components/UI/UploadFile/UploadFile.tsx @@ -1,35 +1,78 @@ -import { styled } from '@mui/material/styles'; import Button from '@mui/material/Button'; import UploadIcon from 'assets/images/icons/UploadIcon.svg?react'; import styles from './UploadFile.module.css'; +import { slicedString } from 'common/utils'; +import { useState } from 'react'; +import { Formik, Form } from 'formik'; +import { useTranslation } from 'react-i18next'; +import { Loading } from '../Layout/Loading/Loading'; +import { CircularProgress } from '@mui/material'; -export const UploadFile = () => { - const VisuallyHiddenInput = styled('input')({ - clip: 'rect(0 0 0 0)', - clipPath: 'inset(50%)', - height: 1, - overflow: 'hidden', - position: 'absolute', - bottom: 0, - left: 0, - whiteSpace: 'nowrap', - width: 1, - }); +export const UploadFile = ({ setFile }: any) => { + const [errors, setErrors] = useState(''); + const [fileName, setFileName] = useState(null); + + const { t } = useTranslation(); + + const addAttachment = (event: any) => { + const media = event.target.files[0]; + + if (media.size / 1000000 > 10) { + setErrors('File size should be less than 10MB'); + return; + } else if (media.type !== 'application/pdf') { + setErrors('File type should be PDF'); + return; + } + + if (media) { + const mediaName = media.name; + const slicedName = slicedString(mediaName, 25); + setFileName(slicedName); + setFile(media); + } + }; return ( - +
+ +

Upload PDF files less than 10 MB.

+

{errors}

+ + ); }; diff --git a/src/containers/KnowledgeBase/KnowledgeBase.tsx b/src/containers/KnowledgeBase/KnowledgeBase.tsx index abacb06a2..8ccfb6a28 100644 --- a/src/containers/KnowledgeBase/KnowledgeBase.tsx +++ b/src/containers/KnowledgeBase/KnowledgeBase.tsx @@ -1,30 +1,57 @@ import { List } from 'containers/List/List'; import { useTranslation } from 'react-i18next'; import CollectionIcon from 'assets/images/icons/Collection/Dark.svg?react'; -import { GET_COLLECTIONS_COUNT, FILTER_COLLECTIONS } from 'graphql/queries/Collection'; +import CopyIcon from 'assets/images/icons/Flow/Copy.svg?react'; +import { GET_KNOWLEDGE_BASE } from 'graphql/queries/KnowledgeBase'; import styles from './Knowledgebase.module.css'; import { DELETE_COLLECTION } from 'graphql/mutations/Collection'; import { useState } from 'react'; import { DialogBox } from 'components/UI/DialogBox/DialogBox'; import { UploadFile } from 'components/UI/UploadFile/UploadFile'; +import { copyToClipboard } from 'common/utils'; +import { useMutation } from '@apollo/client'; +import { UPLOAD_KNOWLEDGE_BASE } from 'graphql/mutations/KnowledgeBase'; +import { setNotification } from 'common/notification'; export const KnowledgeBase = () => { const queries = { - countQuery: GET_COLLECTIONS_COUNT, - filterItemsQuery: FILTER_COLLECTIONS, + filterItemsQuery: GET_KNOWLEDGE_BASE, deleteItemQuery: DELETE_COLLECTION, }; const [dialogOpen, setDialogOpen] = useState(false); + const [file, setFile] = useState(null); + const [uploading, setUploading] = useState(false); const { t } = useTranslation(); - const getLabel = (label: string) =>
{label}
; - const getId = (id: string) =>
{id}
; - const getColumns = ({ label, contactsCount, description, waGroupsCount, id }: any) => { + const [uploadMedia] = useMutation(UPLOAD_KNOWLEDGE_BASE, { + onCompleted: (data: any) => { + setUploading(false); + }, + onError: () => { + setFile(null); + setUploading(false); + setNotification(t('An error occured while uploading the file'), 'warning'); + }, + }); + + const getLabel = (label: string) =>
{label}
; + const getUuid = (id: string) => ( +
+
copyToClipboard(id)}> + +
+ {id} +
+ ); + const getCategory = (category: any) =>
{category?.name}
; + + const getColumns = ({ name, category, uuid }: any) => { return { - label: getLabel(label), - description: getId(id), + uuid: getUuid(uuid), + title: getLabel(name), + category: getCategory(category), }; }; @@ -34,15 +61,27 @@ export const KnowledgeBase = () => { }; const collectionIcon = ; + const uploadFile = async () => { + setUploading(true); + await uploadMedia({ + variables: { + categoryId: '2', + media: file, + }, + }); + }; + const dialog = ( setDialogOpen(false)} + handleOk={uploadFile} buttonOk={t('Upload')} + buttonOkLoading={uploading} alignButtons="center" >
- +
); @@ -51,13 +90,14 @@ export const KnowledgeBase = () => { <> setDialogOpen(true) }} filters={{}} pageLink={`knowledge-base`} diff --git a/src/containers/KnowledgeBase/Knowledgebase.module.css b/src/containers/KnowledgeBase/Knowledgebase.module.css index e69de29bb..21de72700 100644 --- a/src/containers/KnowledgeBase/Knowledgebase.module.css +++ b/src/containers/KnowledgeBase/Knowledgebase.module.css @@ -0,0 +1,32 @@ +.UUID { + color: #555; + font-size: 13px; + font-weight: 500; + min-width: 200px; + width: 60%; + display: flex; + align-items: flex-start; + column-gap: 0.5rem; + cursor: pointer; +} + +.Actions { + min-width: 200px; + text-align: end; + width: 60%; +} + +.Label, +.Category { + color: #555; + font-size: 13px; + font-weight: 500; + margin-bottom: 6px; + width: 40%; +} + +.CopyIcon { + color: #555; + width: 1rem; + height: 1rem; +} diff --git a/src/containers/List/List.tsx b/src/containers/List/List.tsx index 04fc78b35..cfd71cedf 100644 --- a/src/containers/List/List.tsx +++ b/src/containers/List/List.tsx @@ -99,7 +99,7 @@ export interface ListProps { descriptionBox?: any; loadingList?: boolean; columnNames?: Array; - countQuery: DocumentNode; + countQuery?: DocumentNode; listItem: string; filterItemsQuery: DocumentNode; deleteItemQuery: DocumentNode | null; @@ -322,14 +322,20 @@ export const List = ({ }, [searchVal, tableVals, filters]); // Get the total number of items here - const { - loading: l, - error: e, - data: countData, - refetch: refetchCount, - } = useQuery(countQuery, { - variables: { filter }, - }); + + let refetchCount: any; + let countData; + let e; + let l; + + if (countQuery) { + [, { data: countData, error: e, loading: l, refetch: refetchCount }] = useLazyQuery( + countQuery, + { + variables: { filter }, + } + ); + } // Get item data here const [, { loading, error, data, refetch: refetchValues }] = useLazyQuery(filterItemsQuery, { @@ -348,7 +354,9 @@ export const List = ({ useEffect(() => { // Todo: refetching values twice. Need to think of a better way to do this refetchValues(); - refetchCount(); + if (countQuery) { + refetchCount(); + } }, [searchVal, filters, refreshList]); useEffect(() => { @@ -371,7 +379,7 @@ export const List = ({ onCompleted: () => { setNotification(`${capitalListItemName} deleted successfully`); checkUserRole(); - refetchCount(); + countQuery && refetchCount(); if (refetchValues) { refetchValues(filterPayload()); } @@ -640,6 +648,8 @@ export const List = ({ // Get item data and total number of items. let itemList: any = []; if (data) { + console.log(data, listItem); + itemList = formatList(data[listItem]); } @@ -679,6 +689,7 @@ export const List = ({ collapseRow={collapseRow} loadingList={loadingList || loading || l || loadingCollections} noItemsText={noItemsText} + showPagination={countQuery ? true : false} /> ); diff --git a/src/graphql/mutations/KnowledgeBase.ts b/src/graphql/mutations/KnowledgeBase.ts new file mode 100644 index 000000000..5058480c8 --- /dev/null +++ b/src/graphql/mutations/KnowledgeBase.ts @@ -0,0 +1,9 @@ +import { gql } from '@apollo/client'; + +export const UPLOAD_KNOWLEDGE_BASE = gql` + mutation UploadKnowledgeBase($categoryId: ID!, $media: Upload!) { + uploadKnowledgeBase(categoryId: $categoryId, media: $media) { + msg + } + } +`; diff --git a/src/graphql/queries/KnowledgeBase.ts b/src/graphql/queries/KnowledgeBase.ts index 7d256f220..fb9d42287 100644 --- a/src/graphql/queries/KnowledgeBase.ts +++ b/src/graphql/queries/KnowledgeBase.ts @@ -1,21 +1,15 @@ import { gql } from '@apollo/client'; -export const GET_DOCUMENTS = gql` - query getGroup($id: ID!) { - group(id: $id) { - group { +export const GET_KNOWLEDGE_BASE = gql` + query KnowledgeBases { + knowledgeBases { + category { id - label - roles { - id - label - } - description - users { - id - name - } + name + uuid } + name + uuid } } `; From a349423f1c39490f3a388c04684ccce8e8593d18 Mon Sep 17 00:00:00 2001 From: akanshaaaa19 Date: Wed, 24 Apr 2024 22:18:16 +0530 Subject: [PATCH 03/14] connected apis --- src/components/UI/UploadFile/UploadFile.tsx | 22 ++++++++------------- src/containers/List/List.tsx | 2 +- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/components/UI/UploadFile/UploadFile.tsx b/src/components/UI/UploadFile/UploadFile.tsx index 3e35480e8..530bab421 100644 --- a/src/components/UI/UploadFile/UploadFile.tsx +++ b/src/components/UI/UploadFile/UploadFile.tsx @@ -4,28 +4,22 @@ import styles from './UploadFile.module.css'; import { slicedString } from 'common/utils'; import { useState } from 'react'; import { Formik, Form } from 'formik'; -import { useTranslation } from 'react-i18next'; -import { Loading } from '../Layout/Loading/Loading'; -import { CircularProgress } from '@mui/material'; export const UploadFile = ({ setFile }: any) => { const [errors, setErrors] = useState(''); const [fileName, setFileName] = useState(null); - const { t } = useTranslation(); - const addAttachment = (event: any) => { const media = event.target.files[0]; - - if (media.size / 1000000 > 10) { - setErrors('File size should be less than 10MB'); - return; - } else if (media.type !== 'application/pdf') { - setErrors('File type should be PDF'); - return; - } - if (media) { + if (media.size / 1000000 > 10) { + setErrors('File size should be less than 10MB'); + return; + } else if (media.type !== 'application/pdf') { + setErrors('File type should be PDF'); + return; + } + const mediaName = media.name; const slicedName = slicedString(mediaName, 25); setFileName(slicedName); diff --git a/src/containers/List/List.tsx b/src/containers/List/List.tsx index cfd71cedf..83f15a762 100644 --- a/src/containers/List/List.tsx +++ b/src/containers/List/List.tsx @@ -1,7 +1,7 @@ import { useState, useEffect, useCallback, Fragment } from 'react'; import { Link, Navigate, useNavigate, useSearchParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; -import { useQuery, useMutation, DocumentNode, useLazyQuery } from '@apollo/client'; +import { useMutation, DocumentNode, useLazyQuery } from '@apollo/client'; import { Backdrop, Divider, IconButton, Menu, MenuItem } from '@mui/material'; import { Button } from 'components/UI/Form/Button/Button'; From 96a267407274cc1adaa7b0a5fe805dffcff1b664 Mon Sep 17 00:00:00 2001 From: akanshaaaa19 Date: Thu, 25 Apr 2024 15:27:32 +0530 Subject: [PATCH 04/14] categories api and ui changes --- .../UI/UploadFile/UploadFile.module.css | 19 +++++++ src/components/UI/UploadFile/UploadFile.tsx | 53 +++++++++++++++++-- src/config/menu.ts | 2 +- .../AddAttachment/AddAttachment.tsx | 4 +- .../KnowledgeBase/KnowledgeBase.tsx | 45 ++++++++++------ .../KnowledgeBase/Knowledgebase.module.css | 23 +++++--- src/containers/List/List.tsx | 2 - src/graphql/mutations/KnowledgeBase.ts | 8 +++ src/graphql/queries/KnowledgeBase.ts | 10 ++++ 9 files changed, 136 insertions(+), 30 deletions(-) diff --git a/src/components/UI/UploadFile/UploadFile.module.css b/src/components/UI/UploadFile/UploadFile.module.css index 7ab7c94fd..ab5b8fbd4 100644 --- a/src/components/UI/UploadFile/UploadFile.module.css +++ b/src/components/UI/UploadFile/UploadFile.module.css @@ -7,6 +7,7 @@ border-style: dashed; width: 100%; } + .FormError { color: #f44336; font-size: 0.75rem; @@ -45,3 +46,21 @@ font-size: 14px; margin-left: 16px; } + +.FormField { + margin-top: 24px; + vertical-align: middle; + font-weight: 500; + font-size: 16px; + line-height: 18px; + color: #555555; + padding-bottom: 14px; +} + +.Loading { + text-align: center; +} + +.Form { + width: 100%; +} diff --git a/src/components/UI/UploadFile/UploadFile.tsx b/src/components/UI/UploadFile/UploadFile.tsx index 530bab421..46f16f807 100644 --- a/src/components/UI/UploadFile/UploadFile.tsx +++ b/src/components/UI/UploadFile/UploadFile.tsx @@ -3,11 +3,44 @@ import UploadIcon from 'assets/images/icons/UploadIcon.svg?react'; import styles from './UploadFile.module.css'; import { slicedString } from 'common/utils'; import { useState } from 'react'; -import { Formik, Form } from 'formik'; +import { Formik, Form, Field } from 'formik'; +import { Dropdown } from '../Form/Dropdown/Dropdown'; +import { useQuery } from '@apollo/client'; +import { GET_CATEGORIES } from 'graphql/queries/KnowledgeBase'; +import { CircularProgress } from '@mui/material'; -export const UploadFile = ({ setFile }: any) => { +interface UploadFileProps { + setFile: any; + category: string | null; + setCategory: any; +} + +export const UploadFile = ({ setFile, category, setCategory }: UploadFileProps) => { const [errors, setErrors] = useState(''); const [fileName, setFileName] = useState(null); + const [options, setOptions] = useState([]); + + const { loading } = useQuery(GET_CATEGORIES, { + onCompleted: (data) => { + setOptions( + data.categories.map((category: any) => ({ id: category.id, label: category.name })) + ); + }, + }); + + let formFieldItems: any = [ + { + component: Dropdown, + options, + name: 'category', + placeholder: 'Select Category', + fieldValue: category, + fieldChange: (event: any) => { + setCategory(event?.target.value); + setErrors(''); + }, + }, + ]; const addAttachment = (event: any) => { const media = event.target.files[0]; @@ -27,6 +60,10 @@ export const UploadFile = ({ setFile }: any) => { } }; + if (loading) { + return ; + } + return ( { onSubmit={() => {}} >
+
+ {formFieldItems.map((field: any) => ( +
+ +
+ ))} +
{errors}
+
+ +
Document
-

Upload PDF files less than 10 MB.

+

+ Upload PDF files less than 1 MB.
If there are multiple pages please split the PDF. +

{errors}

diff --git a/src/containers/KnowledgeBase/KnowledgeBase.tsx b/src/containers/KnowledgeBase/KnowledgeBase.tsx index 42e8a0a06..338dd3bf0 100644 --- a/src/containers/KnowledgeBase/KnowledgeBase.tsx +++ b/src/containers/KnowledgeBase/KnowledgeBase.tsx @@ -1,6 +1,7 @@ import { List } from 'containers/List/List'; import { useTranslation } from 'react-i18next'; import CollectionIcon from 'assets/images/icons/Collection/Dark.svg?react'; +import DeleteIcon from 'assets/images/icons/Delete/Red.svg?react'; import CopyIcon from 'assets/images/icons/Flow/Copy.svg?react'; import { GET_KNOWLEDGE_BASE } from 'graphql/queries/KnowledgeBase'; import styles from './Knowledgebase.module.css'; @@ -19,6 +20,7 @@ export const KnowledgeBase = () => { }; const [dialogOpen, setDialogOpen] = useState(false); + const [deleteItemId, setDeleteItemID] = useState(null); const [file, setFile] = useState(null); const [category, setCategory] = useState(null); const [uploading, setUploading] = useState(false); @@ -38,6 +40,17 @@ export const KnowledgeBase = () => { }, }); + const [deleteKnowledgeBase, { loading: deleteLoading }] = useMutation(DELETE_KNOWLEDGE_BASE, { + onCompleted: (data: any) => { + setNotification('Successfully deleted the knowledge base!', 'success'); + setDeleteItemID(null); + }, + onError: () => { + setNotification('An error occured while deleteing the knowledge base.', 'warning'); + setDeleteItemID(null); + }, + }); + const getLabel = (label: string) =>
{label}
; const getId = (category: any) => (
@@ -75,6 +88,14 @@ export const KnowledgeBase = () => { }); }; + const handleDelete = () => { + deleteKnowledgeBase({ + variables: { + uuid: deleteItemId, + }, + }); + }; + const dialog = ( { ); - const getDeleteQueryVariables = (id: any) => ({ - uuid: id, - }); + const Deletedialog = ( + setDeleteItemID(null)} + alignButtons="center" + buttonOkLoading={deleteLoading} + > +

{'This knowledge base will be deleted permanently.'}

+
+ ); + + const additionalAction = () => [ + { + label: t('Delete'), + icon: , + parameter: 'id', + dialog: (id: any) => setDeleteItemID(id), + insideMore: false, + }, + ]; + + const restrictedAction = () => ({ delete: false, edit: false }); return ( <> @@ -108,17 +149,17 @@ export const KnowledgeBase = () => { listItemName="knowledgeBases" button={{ show: true, label: 'Upload', action: () => setDialogOpen(true) }} filters={{}} - deleteModifier={{ - variables: getDeleteQueryVariables, - }} pageLink={`knowledge-base`} + additionalAction={additionalAction} listIcon={collectionIcon} editSupport={false} showSearch={false} + restrictedAction={restrictedAction} {...queries} {...columnAttributes} /> {dialogOpen && dialog} + {deleteItemId !== null && Deletedialog} ); }; diff --git a/src/containers/KnowledgeBase/Knowledgebase.module.css b/src/containers/KnowledgeBase/Knowledgebase.module.css index f1e9e05f8..ecffcf282 100644 --- a/src/containers/KnowledgeBase/Knowledgebase.module.css +++ b/src/containers/KnowledgeBase/Knowledgebase.module.css @@ -41,3 +41,10 @@ justify-content: center; width: 100%; } + +.DialogText { + margin-top: 0px; + text-align: center; + font-size: 14px; + color: #073f24; +} From b420732a8644cb35d36bc1f9cb518120c9cf90ab Mon Sep 17 00:00:00 2001 From: akanshaaaa19 Date: Fri, 26 Apr 2024 10:03:48 +0530 Subject: [PATCH 07/14] feature flag enabled --- src/components/UI/UploadFile/UploadFile.tsx | 115 ++++++++++++-------- src/config/menu.ts | 1 + src/graphql/mutations/KnowledgeBase.ts | 10 ++ src/graphql/queries/Organization.ts | 1 + src/services/AuthService.tsx | 3 +- 5 files changed, 82 insertions(+), 48 deletions(-) diff --git a/src/components/UI/UploadFile/UploadFile.tsx b/src/components/UI/UploadFile/UploadFile.tsx index 19664b3c8..4776faf51 100644 --- a/src/components/UI/UploadFile/UploadFile.tsx +++ b/src/components/UI/UploadFile/UploadFile.tsx @@ -4,10 +4,11 @@ import styles from './UploadFile.module.css'; import { slicedString } from 'common/utils'; import { useState } from 'react'; import { Formik, Form, Field } from 'formik'; -import { Dropdown } from '../Form/Dropdown/Dropdown'; -import { useQuery } from '@apollo/client'; +import { useMutation, useQuery } from '@apollo/client'; import { GET_CATEGORIES } from 'graphql/queries/KnowledgeBase'; import { CircularProgress } from '@mui/material'; +import { CREATE_CATEGORY } from 'graphql/mutations/KnowledgeBase'; +import { AutoComplete } from '../Form/AutoComplete/AutoComplete'; interface UploadFileProps { setFile: any; @@ -15,12 +16,12 @@ interface UploadFileProps { setCategory: any; } -export const UploadFile = ({ setFile, category, setCategory }: UploadFileProps) => { +export const UploadFile = ({ setFile, setCategory }: UploadFileProps) => { const [errors, setErrors] = useState(''); const [fileName, setFileName] = useState(null); const [options, setOptions] = useState([]); - const { loading } = useQuery(GET_CATEGORIES, { + const { loading, refetch: refetchcCategories } = useQuery(GET_CATEGORIES, { onCompleted: (data) => { setOptions( data.categories.map((category: any) => ({ id: category.id, label: category.name })) @@ -28,16 +29,33 @@ export const UploadFile = ({ setFile, category, setCategory }: UploadFileProps) }, }); + const [createCategory] = useMutation(CREATE_CATEGORY); + + const handleCreateCategory = async (value: string) => { + return createCategory({ + variables: { + name: value, + }, + }).then((value) => { + refetchcCategories(); + setCategory(value.data.createCategory?.id); + return value.data.createCategory; + }); + }; + let formFieldItems: any = [ { - component: Dropdown, - options, - name: 'category', - placeholder: 'Select Category', - fieldValue: category, - fieldChange: (event: any) => { - setCategory(event?.target.value); - setErrors(''); + component: AutoComplete, + name: 'categoryId', + options: options, + optionLabel: 'label', + disabled: false, + handleCreateItem: handleCreateCategory, + hasCreateOption: true, + multiple: false, + label: 'Category', + onChange: (value: any) => { + setCategory(value?.id); }, }, ]; @@ -74,46 +92,49 @@ export const UploadFile = ({ setFile, category, setCategory }: UploadFileProps)
{formFieldItems.map((field: any) => ( -
+
+
{field.label}
))}
{errors}
- -
Document
- -

- Upload PDF files less than 1 MB.
If there are multiple pages please split the PDF. -

+
+
Document
+ +

+ Upload PDF files less than 1 MB.
If there are multiple pages please split the + PDF. +

+

{errors}

diff --git a/src/config/menu.ts b/src/config/menu.ts index 46e3eac58..8b1cc92e1 100644 --- a/src/config/menu.ts +++ b/src/config/menu.ts @@ -227,6 +227,7 @@ const menus = (): Menu[] => [ type: 'sideDrawer', icon: 'knowledgeBase', roles: allRoles, + show: !getOrganizationServices('llm4devEnabled'), }, ], }, diff --git a/src/graphql/mutations/KnowledgeBase.ts b/src/graphql/mutations/KnowledgeBase.ts index 517e6efc3..58852f0b7 100644 --- a/src/graphql/mutations/KnowledgeBase.ts +++ b/src/graphql/mutations/KnowledgeBase.ts @@ -15,3 +15,13 @@ export const DELETE_KNOWLEDGE_BASE = gql` } } `; + +export const CREATE_CATEGORY = gql` + mutation CreateCategory($name: String!) { + createCategory(name: $name) { + id + name + uuid + } + } +`; diff --git a/src/graphql/queries/Organization.ts b/src/graphql/queries/Organization.ts index a608f979c..b707adfbc 100644 --- a/src/graphql/queries/Organization.ts +++ b/src/graphql/queries/Organization.ts @@ -127,6 +127,7 @@ export const GET_ORGANIZATION_SERVICES = gql` contactProfileEnabled ticketingEnabled whatsappGroupEnabled + llm4devEnabled } } `; diff --git a/src/services/AuthService.tsx b/src/services/AuthService.tsx index 079c3f199..50913b129 100644 --- a/src/services/AuthService.tsx +++ b/src/services/AuthService.tsx @@ -17,7 +17,8 @@ type ServiceType = | 'rolesAndPermission' | 'contactProfileEnabled' | 'ticketingEnabled' - | 'whatsappGroupEnabled'; + | 'whatsappGroupEnabled' + | 'llm4devEnabled'; // get the current authentication session export const getAuthSession = (element?: string) => { From 4d3a526d4c311c132742cd90bc372b057f84ddd1 Mon Sep 17 00:00:00 2001 From: akanshaaaa19 Date: Fri, 26 Apr 2024 10:58:00 +0530 Subject: [PATCH 08/14] added test cases --- .../KnowledgeBase/KnowledgeBase.tsx | 30 +++-- .../KnowledgeBase/Knowledgebase.test.tsx | 114 ++++++++++++++++++ .../UploadFile/UploadFile.module.css | 0 .../KnowledgeBase}/UploadFile/UploadFile.tsx | 13 +- src/mocks/KnowledgeBase.ts | 99 +++++++++++++++ 5 files changed, 238 insertions(+), 18 deletions(-) create mode 100644 src/containers/KnowledgeBase/Knowledgebase.test.tsx rename src/{components/UI => containers/KnowledgeBase}/UploadFile/UploadFile.module.css (100%) rename src/{components/UI => containers/KnowledgeBase}/UploadFile/UploadFile.tsx (92%) create mode 100644 src/mocks/KnowledgeBase.ts diff --git a/src/containers/KnowledgeBase/KnowledgeBase.tsx b/src/containers/KnowledgeBase/KnowledgeBase.tsx index 338dd3bf0..4c08b26e7 100644 --- a/src/containers/KnowledgeBase/KnowledgeBase.tsx +++ b/src/containers/KnowledgeBase/KnowledgeBase.tsx @@ -7,7 +7,7 @@ import { GET_KNOWLEDGE_BASE } from 'graphql/queries/KnowledgeBase'; import styles from './Knowledgebase.module.css'; import { useState } from 'react'; import { DialogBox } from 'components/UI/DialogBox/DialogBox'; -import { UploadFile } from 'components/UI/UploadFile/UploadFile'; +import { UploadFile } from 'containers/KnowledgeBase/UploadFile/UploadFile'; import { copyToClipboard } from 'common/utils'; import { useMutation } from '@apollo/client'; import { DELETE_KNOWLEDGE_BASE, UPLOAD_KNOWLEDGE_BASE } from 'graphql/mutations/KnowledgeBase'; @@ -57,7 +57,7 @@ export const KnowledgeBase = () => {
{category?.id}
copyToClipboard(category.id)}> - +
@@ -79,13 +79,15 @@ export const KnowledgeBase = () => { const collectionIcon = ; const uploadFile = async () => { - setUploading(true); - await uploadMedia({ - variables: { - categoryId: category, - media: file, - }, - }); + if (file && category) { + setUploading(true); + await uploadMedia({ + variables: { + categoryId: category, + media: file, + }, + }); + } }; const handleDelete = () => { @@ -105,8 +107,8 @@ export const KnowledgeBase = () => { buttonOkLoading={uploading} alignButtons="center" > -
- +
+
); @@ -119,14 +121,16 @@ export const KnowledgeBase = () => { alignButtons="center" buttonOkLoading={deleteLoading} > -

{'This knowledge base will be deleted permanently.'}

+

+ {'This knowledge base will be deleted permanently.'} +

); const additionalAction = () => [ { label: t('Delete'), - icon: , + icon: , parameter: 'id', dialog: (id: any) => setDeleteItemID(id), insideMore: false, diff --git a/src/containers/KnowledgeBase/Knowledgebase.test.tsx b/src/containers/KnowledgeBase/Knowledgebase.test.tsx new file mode 100644 index 000000000..f7521d345 --- /dev/null +++ b/src/containers/KnowledgeBase/Knowledgebase.test.tsx @@ -0,0 +1,114 @@ +import { MockedProvider } from '@apollo/client/testing'; +import KnowledgeBase from './KnowledgeBase'; +import { fireEvent, render, screen, waitFor } from '@testing-library/react'; +import { knowledgeBaseMocks } from 'mocks/KnowledgeBase'; +import { MemoryRouter } from 'react-router'; +import * as utils from 'common/utils'; +import * as notification from 'common/notification'; + +const wrapper = ( + + + + + +); + +test('KnowledgeBase component renders', async () => { + const { getByText, getByTestId } = render(wrapper); + + expect(getByTestId('loading')).toBeInTheDocument(); + + await waitFor(() => { + expect(getByText('Knowledge base')).toBeInTheDocument(); + expect(getByText('Document1.pdf')).toBeInTheDocument(); + }); +}); + +test('It copies the category id.', async () => { + const { getByText, getByTestId, getAllByTestId } = render(wrapper); + const copyToClipboardSpy = vi.spyOn(utils, 'copyToClipboard'); + + expect(getByTestId('loading')).toBeInTheDocument(); + + await waitFor(() => { + expect(getByText('Knowledge base')).toBeInTheDocument(); + }); + + fireEvent.click(getAllByTestId('copy-icon')[0]); + + await waitFor(() => { + expect(copyToClipboardSpy).toHaveBeenCalled(); + }); +}); + +test('It deletes the knowledge base', async () => { + const { getByText, getByTestId, getAllByTestId } = render(wrapper); + const notificationSpy = vi.spyOn(notification, 'setNotification'); + + expect(getByTestId('loading')).toBeInTheDocument(); + + await waitFor(() => { + expect(getByText('Knowledge base')).toBeInTheDocument(); + }); + + fireEvent.click(getAllByTestId('delete-icon')[0]); + + await waitFor(() => { + expect(screen.getByTestId('delete-dialog')).toBeInTheDocument(); + }); + + fireEvent.click(screen.getByTestId('ok-button')); + + await waitFor(() => { + expect(notificationSpy).toHaveBeenCalled(); + }); +}); + +test('It opens upload dialog box', async () => { + const { getByText, getByTestId } = render(wrapper); + + expect(getByTestId('loading')).toBeInTheDocument(); + + await waitFor(() => { + expect(getByText('Knowledge base')).toBeInTheDocument(); + }); + + fireEvent.click(getByTestId('newItemButton')); + + await waitFor(() => { + expect(screen.getByTestId('upload-dialog')).toBeInTheDocument(); + }); +}); + +test.only('it uploads the file', async () => { + const { getByTestId } = render(wrapper); + const notificationSpy = vi.spyOn(notification, 'setNotification'); + + expect(getByTestId('loading')).toBeInTheDocument(); + + fireEvent.click(getByTestId('newItemButton')); + + await waitFor(() => { + expect(screen.getByText('Upload File')).toBeInTheDocument(); + }); + + const categoryDropDown = screen.getByRole('combobox'); + + categoryDropDown.focus(); + fireEvent.keyDown(categoryDropDown, { key: 'ArrowDown' }); + fireEvent.keyDown(categoryDropDown, { key: 'ArrowDown' }); + fireEvent.keyDown(categoryDropDown, { key: 'Enter' }); + + fireEvent.change(screen.getByTestId('uploadFile'), { + target: { + files: [new File(['(⌐□_□)'], 'file.pdf', { type: 'application/pdf' })], + }, + }); + + fireEvent.click(screen.getByTestId('ok-button')); + + await waitFor(() => { + expect(notificationSpy).toHaveBeenCalled(); + }); +}); diff --git a/src/components/UI/UploadFile/UploadFile.module.css b/src/containers/KnowledgeBase/UploadFile/UploadFile.module.css similarity index 100% rename from src/components/UI/UploadFile/UploadFile.module.css rename to src/containers/KnowledgeBase/UploadFile/UploadFile.module.css diff --git a/src/components/UI/UploadFile/UploadFile.tsx b/src/containers/KnowledgeBase/UploadFile/UploadFile.tsx similarity index 92% rename from src/components/UI/UploadFile/UploadFile.tsx rename to src/containers/KnowledgeBase/UploadFile/UploadFile.tsx index 4776faf51..0c1c3c0c8 100644 --- a/src/components/UI/UploadFile/UploadFile.tsx +++ b/src/containers/KnowledgeBase/UploadFile/UploadFile.tsx @@ -8,12 +8,14 @@ import { useMutation, useQuery } from '@apollo/client'; import { GET_CATEGORIES } from 'graphql/queries/KnowledgeBase'; import { CircularProgress } from '@mui/material'; import { CREATE_CATEGORY } from 'graphql/mutations/KnowledgeBase'; -import { AutoComplete } from '../Form/AutoComplete/AutoComplete'; +import { AutoComplete } from '../../../components/UI/Form/AutoComplete/AutoComplete'; +import * as Yup from 'yup'; interface UploadFileProps { setFile: any; category: string | null; setCategory: any; + file: any; } export const UploadFile = ({ setFile, setCategory }: UploadFileProps) => { @@ -21,7 +23,7 @@ export const UploadFile = ({ setFile, setCategory }: UploadFileProps) => { const [fileName, setFileName] = useState(null); const [options, setOptions] = useState([]); - const { loading, refetch: refetchcCategories } = useQuery(GET_CATEGORIES, { + const { loading, refetch: refetchCategories } = useQuery(GET_CATEGORIES, { onCompleted: (data) => { setOptions( data.categories.map((category: any) => ({ id: category.id, label: category.name })) @@ -37,7 +39,7 @@ export const UploadFile = ({ setFile, setCategory }: UploadFileProps) => { name: value, }, }).then((value) => { - refetchcCategories(); + refetchCategories(); setCategory(value.data.createCategory?.id); return value.data.createCategory; }); @@ -79,13 +81,14 @@ export const UploadFile = ({ setFile, setCategory }: UploadFileProps) => { }; if (loading) { - return ; + return ; } return ( {}} > diff --git a/src/mocks/KnowledgeBase.ts b/src/mocks/KnowledgeBase.ts new file mode 100644 index 000000000..3bcbf9569 --- /dev/null +++ b/src/mocks/KnowledgeBase.ts @@ -0,0 +1,99 @@ +import { DELETE_KNOWLEDGE_BASE, UPLOAD_KNOWLEDGE_BASE } from 'graphql/mutations/KnowledgeBase'; +import { GET_CATEGORIES, GET_KNOWLEDGE_BASE } from 'graphql/queries/KnowledgeBase'; + +const getKnowledgeBase = { + request: { + query: GET_KNOWLEDGE_BASE, + variables: { filter: {}, opts: { limit: 50, offset: 0, order: 'ASC', orderWith: undefined } }, + }, + result: { + data: { + knowledgeBases: [ + { + __typename: 'KnowledgeBase', + category: { + __typename: 'Category', + id: '1', + name: 'Support', + uuid: '3fa22108-f464-41e5-81d9-d8a298854429', + }, + id: 'e3f282c4-278a-43b9-ac81-fc31dac8a749', + name: 'Document1.pdf', + }, + { + __typename: 'KnowledgeBase', + category: { + __typename: 'Category', + id: '2', + name: 'Support', + uuid: '3fa22108-f464-41e5-81d9-d8a298854429', + }, + id: '5252b667-7289-4e06-95fd-0de66eea78ae', + name: 'Document2.pdf', + }, + ], + }, + }, +}; + +const deleteKnowledgeBase = { + request: { + query: DELETE_KNOWLEDGE_BASE, + variables: { uuid: 'e3f282c4-278a-43b9-ac81-fc31dac8a749' }, + }, + result: { + data: { + deleteKnowledgeBase: { + msg: 'Successfully Deleted', + }, + }, + }, +}; + +const getCategories = { + request: { + query: GET_CATEGORIES, + }, + result: { + data: { + categories: [ + { + __typename: 'Category', + id: '1', + name: 'Support', + uuid: '3fa22108-f464-41e5-81d9-d8a298854429', + }, + { + __typename: 'Category', + id: '2', + name: 'Support', + uuid: '3fa22108-f464-41e5-81d9-d8a298854429', + }, + ], + }, + }, +}; + +const uploadKnowledgeBase = { + request: { + query: UPLOAD_KNOWLEDGE_BASE, + }, + result: { + data: { + uploadKnowledgeBase: { + msg: 'Successfully uploaded the file!', + }, + }, + }, + variableMatcher: (variables: any) => true, +}; + +export const knowledgeBaseMocks = [ + getKnowledgeBase, + getKnowledgeBase, + deleteKnowledgeBase, + getKnowledgeBase, + getCategories, + uploadKnowledgeBase, + uploadKnowledgeBase, +]; From 6aa1da9e8411eb61aa1de5f87425692ad7e7479a Mon Sep 17 00:00:00 2001 From: akanshaaaa19 Date: Fri, 26 Apr 2024 11:03:41 +0530 Subject: [PATCH 09/14] removed .only --- src/containers/KnowledgeBase/Knowledgebase.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/containers/KnowledgeBase/Knowledgebase.test.tsx b/src/containers/KnowledgeBase/Knowledgebase.test.tsx index f7521d345..bbf64d7f4 100644 --- a/src/containers/KnowledgeBase/Knowledgebase.test.tsx +++ b/src/containers/KnowledgeBase/Knowledgebase.test.tsx @@ -81,7 +81,7 @@ test('It opens upload dialog box', async () => { }); }); -test.only('it uploads the file', async () => { +test('it uploads the file', async () => { const { getByTestId } = render(wrapper); const notificationSpy = vi.spyOn(notification, 'setNotification'); From dd960fe48db85fee91be3fe73da9e9e8dbee1a71 Mon Sep 17 00:00:00 2001 From: akanshaaaa19 Date: Fri, 26 Apr 2024 11:36:57 +0530 Subject: [PATCH 10/14] added test cases --- .../UploadFile/UploadFile.module.css | 1 + .../UploadFile/UploadFile.test.tsx | 70 +++++++++++++++++++ .../KnowledgeBase/UploadFile/UploadFile.tsx | 17 ++--- src/mocks/KnowledgeBase.ts | 26 ++++++- 4 files changed, 105 insertions(+), 9 deletions(-) create mode 100644 src/containers/KnowledgeBase/UploadFile/UploadFile.test.tsx diff --git a/src/containers/KnowledgeBase/UploadFile/UploadFile.module.css b/src/containers/KnowledgeBase/UploadFile/UploadFile.module.css index ab5b8fbd4..852580e72 100644 --- a/src/containers/KnowledgeBase/UploadFile/UploadFile.module.css +++ b/src/containers/KnowledgeBase/UploadFile/UploadFile.module.css @@ -14,6 +14,7 @@ text-align: left; font-weight: 400; margin: 0; + padding: 0 8px; } .Container input { diff --git a/src/containers/KnowledgeBase/UploadFile/UploadFile.test.tsx b/src/containers/KnowledgeBase/UploadFile/UploadFile.test.tsx new file mode 100644 index 000000000..a4afc3f3b --- /dev/null +++ b/src/containers/KnowledgeBase/UploadFile/UploadFile.test.tsx @@ -0,0 +1,70 @@ +import { MockedProvider } from '@apollo/client/testing'; +import { fireEvent, render, screen, waitFor } from '@testing-library/react'; +import { categoryMocks } from 'mocks/KnowledgeBase'; +import { MemoryRouter } from 'react-router'; +import { UploadFile } from './UploadFile'; + +const defaultProps = { + setFile: vi.fn(), + setCategory: vi.fn(), + file: null, + category: null, +}; + +const wrapper = ( + + + + + +); + +test('it creates new category', async () => { + const { getByText, getByTestId } = render(wrapper); + + expect(getByTestId('loading')).toBeInTheDocument(); + + await waitFor(() => { + expect(getByText('Upload File')).toBeInTheDocument(); + }); + + const categoryDropDown = screen.getByRole('combobox'); + + categoryDropDown.focus(); + fireEvent.keyDown(categoryDropDown, { key: 'ArrowDown' }); + fireEvent.change(categoryDropDown, { target: { value: 'Support2' } }); + fireEvent.keyDown(categoryDropDown, { key: 'ArrowDown' }); + fireEvent.keyDown(categoryDropDown, { key: 'Enter' }); +}); + +test('shows errors for invalid file', async () => { + const { getByText, getByTestId } = render(wrapper); + const content = (size: number) => new Blob([new Array(size).fill(' ').join('')]); + const mockFile = (type: string, size: number) => new File([content(size)], 'file.pdf', { type }); + + expect(getByTestId('loading')).toBeInTheDocument(); + + await waitFor(() => { + expect(getByText('Upload File')).toBeInTheDocument(); + }); + + fireEvent.change(screen.getByTestId('uploadFile'), { + target: { + files: [mockFile('application/pdf', 1700000)], + }, + }); + + await waitFor(() => { + expect(screen.getAllByText('File size should be less than 1MB')[0]).toBeInTheDocument(); + }); + + fireEvent.change(screen.getByTestId('uploadFile'), { + target: { + files: [mockFile('image/png', 1024)], + }, + }); + + await waitFor(() => { + expect(screen.getAllByText('File type should be PDF')[0]).toBeInTheDocument(); + }); +}); diff --git a/src/containers/KnowledgeBase/UploadFile/UploadFile.tsx b/src/containers/KnowledgeBase/UploadFile/UploadFile.tsx index 0c1c3c0c8..f103b9e96 100644 --- a/src/containers/KnowledgeBase/UploadFile/UploadFile.tsx +++ b/src/containers/KnowledgeBase/UploadFile/UploadFile.tsx @@ -9,7 +9,6 @@ import { GET_CATEGORIES } from 'graphql/queries/KnowledgeBase'; import { CircularProgress } from '@mui/material'; import { CREATE_CATEGORY } from 'graphql/mutations/KnowledgeBase'; import { AutoComplete } from '../../../components/UI/Form/AutoComplete/AutoComplete'; -import * as Yup from 'yup'; interface UploadFileProps { setFile: any; @@ -41,7 +40,7 @@ export const UploadFile = ({ setFile, setCategory }: UploadFileProps) => { }).then((value) => { refetchCategories(); setCategory(value.data.createCategory?.id); - return value.data.createCategory; + return { id: value.data.createCategory.id, label: value.data.createCategory.name }; }); }; @@ -62,11 +61,11 @@ export const UploadFile = ({ setFile, setCategory }: UploadFileProps) => { }, ]; - const addAttachment = (event: any) => { + const addFile = (event: any) => { const media = event.target.files[0]; if (media) { - if (media.size / 1000000 > 10) { - setErrors('File size should be less than 10MB'); + if (media.size / 1000000 > 1) { + setErrors('File size should be less than 1MB'); return; } else if (media.type !== 'application/pdf') { setErrors('File type should be PDF'); @@ -100,7 +99,6 @@ export const UploadFile = ({ setFile, setCategory }: UploadFileProps) => {
))} -
{errors}
Document
@@ -128,7 +126,8 @@ export const UploadFile = ({ setFile, setCategory }: UploadFileProps) => { data-testid="uploadFile" name="file" onChange={(event) => { - addAttachment(event); + setErrors(''); + addFile(event); }} />
@@ -138,7 +137,9 @@ export const UploadFile = ({ setFile, setCategory }: UploadFileProps) => { PDF.

-

{errors}

+

+ {errors} +

); diff --git a/src/mocks/KnowledgeBase.ts b/src/mocks/KnowledgeBase.ts index 3bcbf9569..8301e81c4 100644 --- a/src/mocks/KnowledgeBase.ts +++ b/src/mocks/KnowledgeBase.ts @@ -1,4 +1,8 @@ -import { DELETE_KNOWLEDGE_BASE, UPLOAD_KNOWLEDGE_BASE } from 'graphql/mutations/KnowledgeBase'; +import { + CREATE_CATEGORY, + DELETE_KNOWLEDGE_BASE, + UPLOAD_KNOWLEDGE_BASE, +} from 'graphql/mutations/KnowledgeBase'; import { GET_CATEGORIES, GET_KNOWLEDGE_BASE } from 'graphql/queries/KnowledgeBase'; const getKnowledgeBase = { @@ -88,6 +92,26 @@ const uploadKnowledgeBase = { variableMatcher: (variables: any) => true, }; +const createCategory = { + request: { + query: CREATE_CATEGORY, + variables: { + name: 'Support2', + }, + }, + result: { + data: { + createCategory: { + id: '2', + name: 'Support2', + uuid: '3fa22108-f464-41e5-81d9-d8a298854429', + }, + }, + }, +}; + +export const categoryMocks = [createCategory, getCategories, getCategories]; + export const knowledgeBaseMocks = [ getKnowledgeBase, getKnowledgeBase, From 202b0ec977929b3df3fc9c594c296d1b178f26c1 Mon Sep 17 00:00:00 2001 From: akanshaaaa19 Date: Fri, 26 Apr 2024 11:47:14 +0530 Subject: [PATCH 11/14] added test cases --- .../KnowledgeBase/KnowledgeBase.tsx | 12 ++++++--- .../KnowledgeBase/Knowledgebase.test.tsx | 25 ++++++++++++++++--- .../KnowledgeBase/UploadFile/UploadFile.tsx | 2 +- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/containers/KnowledgeBase/KnowledgeBase.tsx b/src/containers/KnowledgeBase/KnowledgeBase.tsx index 4c08b26e7..b78365682 100644 --- a/src/containers/KnowledgeBase/KnowledgeBase.tsx +++ b/src/containers/KnowledgeBase/KnowledgeBase.tsx @@ -29,9 +29,8 @@ export const KnowledgeBase = () => { const [uploadMedia] = useMutation(UPLOAD_KNOWLEDGE_BASE, { onCompleted: (data: any) => { - setUploading(false); setNotification('Successfully uploaded the file!', 'success'); - setDialogOpen(false); + handleClose(); }, onError: () => { setFile(null); @@ -90,6 +89,13 @@ export const KnowledgeBase = () => { } }; + const handleClose = () => { + setFile(null); + setCategory(null); + setDialogOpen(false); + setUploading(false); + }; + const handleDelete = () => { deleteKnowledgeBase({ variables: { @@ -101,7 +107,7 @@ export const KnowledgeBase = () => { const dialog = ( setDialogOpen(false)} + handleCancel={handleClose} handleOk={uploadFile} buttonOk={t('Upload')} buttonOkLoading={uploading} diff --git a/src/containers/KnowledgeBase/Knowledgebase.test.tsx b/src/containers/KnowledgeBase/Knowledgebase.test.tsx index bbf64d7f4..f65455e73 100644 --- a/src/containers/KnowledgeBase/Knowledgebase.test.tsx +++ b/src/containers/KnowledgeBase/Knowledgebase.test.tsx @@ -42,7 +42,7 @@ test('It copies the category id.', async () => { }); }); -test('It deletes the knowledge base', async () => { +test('It deletes the knowledge base and closes the dialog box', async () => { const { getByText, getByTestId, getAllByTestId } = render(wrapper); const notificationSpy = vi.spyOn(notification, 'setNotification'); @@ -54,8 +54,10 @@ test('It deletes the knowledge base', async () => { fireEvent.click(getAllByTestId('delete-icon')[0]); + const dialog = screen.getByTestId('delete-dialog'); + await waitFor(() => { - expect(screen.getByTestId('delete-dialog')).toBeInTheDocument(); + expect(dialog).toBeInTheDocument(); }); fireEvent.click(screen.getByTestId('ok-button')); @@ -63,9 +65,16 @@ test('It deletes the knowledge base', async () => { await waitFor(() => { expect(notificationSpy).toHaveBeenCalled(); }); + + fireEvent.click(getAllByTestId('delete-icon')[0]); + fireEvent.click(getByTestId('cancel-button')); + + await waitFor(() => { + expect(dialog).not.toBeInTheDocument(); + }); }); -test('It opens upload dialog box', async () => { +test('It opens and closes upload dialog box', async () => { const { getByText, getByTestId } = render(wrapper); expect(getByTestId('loading')).toBeInTheDocument(); @@ -76,8 +85,16 @@ test('It opens upload dialog box', async () => { fireEvent.click(getByTestId('newItemButton')); + const dialog = screen.getByTestId('upload-dialog'); + + await waitFor(() => { + expect(dialog).toBeInTheDocument(); + }); + + fireEvent.click(getByTestId('cancel-button')); + await waitFor(() => { - expect(screen.getByTestId('upload-dialog')).toBeInTheDocument(); + expect(dialog).not.toBeInTheDocument(); }); }); diff --git a/src/containers/KnowledgeBase/UploadFile/UploadFile.tsx b/src/containers/KnowledgeBase/UploadFile/UploadFile.tsx index f103b9e96..bbf95b53c 100644 --- a/src/containers/KnowledgeBase/UploadFile/UploadFile.tsx +++ b/src/containers/KnowledgeBase/UploadFile/UploadFile.tsx @@ -39,7 +39,7 @@ export const UploadFile = ({ setFile, setCategory }: UploadFileProps) => { }, }).then((value) => { refetchCategories(); - setCategory(value.data.createCategory?.id); + setCategory(value.data.createCategory.id); return { id: value.data.createCategory.id, label: value.data.createCategory.name }; }); }; From cc131638019487047c2efd61d158a03a9348225f Mon Sep 17 00:00:00 2001 From: akanshaaaa19 Date: Fri, 26 Apr 2024 12:20:26 +0530 Subject: [PATCH 12/14] fixed width --- .../images/icons/SideDrawer/KnowledgeBaseIcon.tsx | 15 --------------- src/components/UI/ListIcon/ListIcon.tsx | 3 +-- .../KnowledgeBase/Knowledgebase.module.css | 6 +++++- 3 files changed, 6 insertions(+), 18 deletions(-) delete mode 100644 src/assets/images/icons/SideDrawer/KnowledgeBaseIcon.tsx diff --git a/src/assets/images/icons/SideDrawer/KnowledgeBaseIcon.tsx b/src/assets/images/icons/SideDrawer/KnowledgeBaseIcon.tsx deleted file mode 100644 index 252d8f93a..000000000 --- a/src/assets/images/icons/SideDrawer/KnowledgeBaseIcon.tsx +++ /dev/null @@ -1,15 +0,0 @@ -export const KnowledgeBaseIcon = ({ color }: any) => { - return ( - - {'Group'} - - - - - - - - ); -}; - -export default KnowledgeBaseIcon; diff --git a/src/components/UI/ListIcon/ListIcon.tsx b/src/components/UI/ListIcon/ListIcon.tsx index 7cfc3af0d..4da383377 100644 --- a/src/components/UI/ListIcon/ListIcon.tsx +++ b/src/components/UI/ListIcon/ListIcon.tsx @@ -27,7 +27,6 @@ import ConsultingIcon from 'assets/images/icons/SideDrawer/ConsultingIcon'; import WaChatIcon from 'assets/images/icons/SideDrawer/WaGroupChat'; import WaCollectionIcon from 'assets/images/icons/SideDrawer/WaGroupCollection'; import WaGroupIcon from 'assets/images/icons/SideDrawer/WhatsAppGroupIcon'; -import KnowledgeBaseIcon from 'assets/images/icons/SideDrawer/KnowledgeBaseIcon'; import styles from './ListIcon.module.css'; import FiberNewIcon from '@mui/icons-material/FiberNew'; import { Badge } from '@mui/material'; @@ -72,7 +71,7 @@ export const ListIcon = ({ icon = '', selected = false, count }: ListIconProps) waGroupCollection: WaCollectionIcon, waGroupChat: WaChatIcon, waGroup: WaGroupIcon, - knowledgeBase: KnowledgeBaseIcon, + knowledgeBase: SheetsIcon, }; const iconImage = stringsToIcons[icon] && ( diff --git a/src/containers/KnowledgeBase/Knowledgebase.module.css b/src/containers/KnowledgeBase/Knowledgebase.module.css index ecffcf282..31f4d875b 100644 --- a/src/containers/KnowledgeBase/Knowledgebase.module.css +++ b/src/containers/KnowledgeBase/Knowledgebase.module.css @@ -2,7 +2,8 @@ color: #555; font-size: 13px; font-weight: 500; - /* min-width: 200px; */ + width: 14%; + min-width: 200px; } .IdContainer { @@ -12,6 +13,7 @@ } .Actions { + width: 15%; min-width: 200px; text-align: end; color: #555; @@ -26,6 +28,8 @@ font-size: 13px; font-weight: 500; margin-bottom: 6px; + width: 40%; + min-width: 200px; } .CopyIcon { From aa941a1666cbba7f9f557296f13d4490881688f7 Mon Sep 17 00:00:00 2001 From: akanshaaaa19 Date: Fri, 26 Apr 2024 15:34:10 +0530 Subject: [PATCH 13/14] resolved comments --- .../icons/SideDrawer/KnowledgeBaseIcon.tsx | 17 +++++++++++++++++ src/components/UI/ListIcon/ListIcon.tsx | 3 ++- .../UploadFile/UploadFile.module.css | 2 +- 3 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 src/assets/images/icons/SideDrawer/KnowledgeBaseIcon.tsx diff --git a/src/assets/images/icons/SideDrawer/KnowledgeBaseIcon.tsx b/src/assets/images/icons/SideDrawer/KnowledgeBaseIcon.tsx new file mode 100644 index 000000000..ecd83fefb --- /dev/null +++ b/src/assets/images/icons/SideDrawer/KnowledgeBaseIcon.tsx @@ -0,0 +1,17 @@ +const SvgComponent = ({ color }: { color: string }) => ( + + + + + + + +); +export default SvgComponent; diff --git a/src/components/UI/ListIcon/ListIcon.tsx b/src/components/UI/ListIcon/ListIcon.tsx index 4da383377..7cfc3af0d 100644 --- a/src/components/UI/ListIcon/ListIcon.tsx +++ b/src/components/UI/ListIcon/ListIcon.tsx @@ -27,6 +27,7 @@ import ConsultingIcon from 'assets/images/icons/SideDrawer/ConsultingIcon'; import WaChatIcon from 'assets/images/icons/SideDrawer/WaGroupChat'; import WaCollectionIcon from 'assets/images/icons/SideDrawer/WaGroupCollection'; import WaGroupIcon from 'assets/images/icons/SideDrawer/WhatsAppGroupIcon'; +import KnowledgeBaseIcon from 'assets/images/icons/SideDrawer/KnowledgeBaseIcon'; import styles from './ListIcon.module.css'; import FiberNewIcon from '@mui/icons-material/FiberNew'; import { Badge } from '@mui/material'; @@ -71,7 +72,7 @@ export const ListIcon = ({ icon = '', selected = false, count }: ListIconProps) waGroupCollection: WaCollectionIcon, waGroupChat: WaChatIcon, waGroup: WaGroupIcon, - knowledgeBase: SheetsIcon, + knowledgeBase: KnowledgeBaseIcon, }; const iconImage = stringsToIcons[icon] && ( diff --git a/src/containers/KnowledgeBase/UploadFile/UploadFile.module.css b/src/containers/KnowledgeBase/UploadFile/UploadFile.module.css index 852580e72..687b1b7cb 100644 --- a/src/containers/KnowledgeBase/UploadFile/UploadFile.module.css +++ b/src/containers/KnowledgeBase/UploadFile/UploadFile.module.css @@ -37,7 +37,7 @@ margin: 0px; padding: 0px; color: #93a29b; - line-height: 1; + line-height: 1.5; font-size: 12px; margin-top: 7px; padding: 0 8px; From 95d5524fb3e62b3cae6864b7bbf346c934761ca9 Mon Sep 17 00:00:00 2001 From: akanshaaaa19 Date: Fri, 26 Apr 2024 15:37:21 +0530 Subject: [PATCH 14/14] resolved comments --- .../images/icons/SideDrawer/KnowledgeBaseIcon.tsx | 2 +- src/containers/KnowledgeBase/UploadFile/UploadFile.tsx | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/assets/images/icons/SideDrawer/KnowledgeBaseIcon.tsx b/src/assets/images/icons/SideDrawer/KnowledgeBaseIcon.tsx index ecd83fefb..9731673c6 100644 --- a/src/assets/images/icons/SideDrawer/KnowledgeBaseIcon.tsx +++ b/src/assets/images/icons/SideDrawer/KnowledgeBaseIcon.tsx @@ -7,7 +7,7 @@ const SvgComponent = ({ color }: { color: string }) => ( height="18" viewBox="0,0,256,256" > - + diff --git a/src/containers/KnowledgeBase/UploadFile/UploadFile.tsx b/src/containers/KnowledgeBase/UploadFile/UploadFile.tsx index bbf95b53c..53ee8ee44 100644 --- a/src/containers/KnowledgeBase/UploadFile/UploadFile.tsx +++ b/src/containers/KnowledgeBase/UploadFile/UploadFile.tsx @@ -132,14 +132,14 @@ export const UploadFile = ({ setFile, setCategory }: UploadFileProps) => { /> +

+ {errors} +

- Upload PDF files less than 1 MB.
If there are multiple pages please split the - PDF. + Please upload a single page PDF.
+ Size should not exceed 1MB.

-

- {errors} -

);