diff --git a/client/src/components/CreatableSelect/CreatableSelect.tsx b/client/src/components/CreatableSelect/CreatableSelect.tsx index 5eb1ad5ef8e..508b3790b5d 100644 --- a/client/src/components/CreatableSelect/CreatableSelect.tsx +++ b/client/src/components/CreatableSelect/CreatableSelect.tsx @@ -18,6 +18,7 @@ type CreatableSelectProps = { const CreatableSelect: React.FC = ({ error, label, + className, name, ...props }) => ( @@ -39,6 +40,7 @@ const CreatableSelect: React.FC = ({ > diff --git a/client/src/components/IntegrationItem/Footer.tsx b/client/src/components/IntegrationItem/Footer.tsx deleted file mode 100644 index 14a6a8dbba9..00000000000 --- a/client/src/components/IntegrationItem/Footer.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import React from 'react'; -import {Category} from './Category'; -import {Date} from './Date'; -import {TagList} from './TagList'; -import {CategoryModel, TagModel} from '../../data-access/integration'; - -const Footer: React.FC<{ - category?: CategoryModel; - tags?: TagModel[]; - date?: Date; -}> = ({category, tags, date}) => { - return ( -
- {category && } - - {tags && } - - {date && } -
- ); -}; - -export default Footer; diff --git a/client/src/components/IntegrationItem/IntegrationItem.tsx b/client/src/components/IntegrationItem/IntegrationItem.tsx deleted file mode 100644 index 7a22364f13c..00000000000 --- a/client/src/components/IntegrationItem/IntegrationItem.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; -import Footer from './Footer'; -import Header from './Header'; -import {CategoryModel, TagModel} from '../../data-access/integration'; - -export const IntegrationItem: React.FC<{ - button: string; - name: string; - status: boolean; - description?: string; - category?: CategoryModel; - date?: Date; - id?: number; - tags?: TagModel[]; - workflowIds?: string[]; -}> = ({id, name, status, description, category, tags, date}) => { - return ( -
-
- -
-
- ); -}; diff --git a/client/src/components/IntegrationItem/Tag.tsx b/client/src/components/IntegrationItem/Tag.tsx deleted file mode 100644 index e4b28d719bd..00000000000 --- a/client/src/components/IntegrationItem/Tag.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; -import {Cross1Icon} from '@radix-ui/react-icons'; -import {TagModel} from '../../data-access/integration'; - -export const Tag: React.FC<{tag: TagModel}> = ({tag}) => { - return ( - - {tag.name} - - - - ); -}; diff --git a/client/src/components/IntegrationItem/TagList.tsx b/client/src/components/IntegrationItem/TagList.tsx deleted file mode 100644 index 7bc45958e06..00000000000 --- a/client/src/components/IntegrationItem/TagList.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; - -import {Tag} from './Tag'; -import {PlusIcon} from '@radix-ui/react-icons'; -import {TagModel} from '../../data-access/integration'; - -export const TagList: React.FC<{tags: TagModel[]}> = ({tags}) => { - return ( -
- {tags.map((tag) => ( - - ))} - -
- -
-
- ); -}; diff --git a/client/src/mutations/integrations.mutations.ts b/client/src/mutations/integrations.mutations.ts index 3974ce67e80..e986766e636 100644 --- a/client/src/mutations/integrations.mutations.ts +++ b/client/src/mutations/integrations.mutations.ts @@ -1,5 +1,9 @@ import {useMutation} from '@tanstack/react-query'; -import {IntegrationModel, IntegrationsApi} from 'data-access/integration'; +import { + IntegrationModel, + IntegrationsApi, + PutIntegrationTagsRequest, +} from 'data-access/integration'; type MutationProps = { onSuccess?: (result: IntegrationModel, variables: IntegrationModel) => void; @@ -16,3 +20,10 @@ export const useIntegrationMutation = (mutationProps?: MutationProps) => onSuccess: mutationProps?.onSuccess, onError: mutationProps?.onError, }); + +export const useIntegrationTagsMutation = () => + useMutation({ + mutationFn: (request: PutIntegrationTagsRequest) => { + return new IntegrationsApi().putIntegrationTags(request); + }, + }); diff --git a/client/src/components/IntegrationItem/AddTagButton.tsx b/client/src/pages/integrations/IntegrationItem/AddTagButton.tsx similarity index 100% rename from client/src/components/IntegrationItem/AddTagButton.tsx rename to client/src/pages/integrations/IntegrationItem/AddTagButton.tsx diff --git a/client/src/components/IntegrationItem/Category.tsx b/client/src/pages/integrations/IntegrationItem/Category.tsx similarity index 87% rename from client/src/components/IntegrationItem/Category.tsx rename to client/src/pages/integrations/IntegrationItem/Category.tsx index b6d7095f3bb..9f588366206 100644 --- a/client/src/components/IntegrationItem/Category.tsx +++ b/client/src/pages/integrations/IntegrationItem/Category.tsx @@ -1,6 +1,6 @@ import {Squares2X2Icon} from '@heroicons/react/24/outline'; import React from 'react'; -import {CategoryModel} from '../../data-access/integration'; +import {CategoryModel} from '../../../data-access/integration'; export const Category: React.FC<{category: CategoryModel}> = ({category}) => { return ( diff --git a/client/src/components/IntegrationItem/Date.tsx b/client/src/pages/integrations/IntegrationItem/Date.tsx similarity index 100% rename from client/src/components/IntegrationItem/Date.tsx rename to client/src/pages/integrations/IntegrationItem/Date.tsx diff --git a/client/src/pages/integrations/IntegrationItem/Footer.tsx b/client/src/pages/integrations/IntegrationItem/Footer.tsx new file mode 100644 index 00000000000..0b18d88aea6 --- /dev/null +++ b/client/src/pages/integrations/IntegrationItem/Footer.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import {Category} from './Category'; +import {Date} from './Date'; +import {TagList} from './TagList'; +import {CategoryModel, TagModel} from '../../../data-access/integration'; + +const Footer: React.FC<{ + category?: CategoryModel; + tags?: TagModel[]; + date?: Date; + remainingTags?: TagModel[]; + onAddTag: (newTag: TagModel) => void; + onDeleteTag: (deletedTag: TagModel) => void; +}> = ({category, tags, date, remainingTags, onAddTag, onDeleteTag}) => { + return ( +
+ {category && } + + {tags && ( + + )} + + {date && } +
+ ); +}; + +export default Footer; diff --git a/client/src/components/IntegrationItem/Header.tsx b/client/src/pages/integrations/IntegrationItem/Header.tsx similarity index 88% rename from client/src/components/IntegrationItem/Header.tsx rename to client/src/pages/integrations/IntegrationItem/Header.tsx index e6747d58600..74061b5f223 100644 --- a/client/src/components/IntegrationItem/Header.tsx +++ b/client/src/pages/integrations/IntegrationItem/Header.tsx @@ -1,7 +1,10 @@ import React from 'react'; import {Name} from './Name'; import {Status} from './Status'; -import {Dropdown, DropDownMenuItem} from '../DropDown/Dropdown'; +import { + Dropdown, + DropDownMenuItem, +} from '../../../components/DropDown/Dropdown'; const menuItems: DropDownMenuItem[] = [ { @@ -32,7 +35,7 @@ const Header: React.FC<{ }> = ({id, name, status, description = 'Description not available'}) => { return (
-
+
diff --git a/client/src/pages/integrations/IntegrationItem/IntegrationItem.tsx b/client/src/pages/integrations/IntegrationItem/IntegrationItem.tsx new file mode 100644 index 00000000000..b5c32fcbb87 --- /dev/null +++ b/client/src/pages/integrations/IntegrationItem/IntegrationItem.tsx @@ -0,0 +1,84 @@ +import React, {useEffect, useState} from 'react'; +import Footer from './Footer'; +import Header from './Header'; +import {CategoryModel, TagModel} from '../../../data-access/integration'; +import {useIntegrationTagsMutation} from 'mutations/integrations.mutations'; + +export const IntegrationItem: React.FC<{ + button: string; + name: string; + status: boolean; + onChangeState: () => void; + description?: string; + category?: CategoryModel; + date?: Date; + id?: number; + tags?: Array; + workflowIds?: string[]; + remainingTags?: TagModel[]; +}> = ({ + id, + name, + status, + description, + category, + tags, + date, + remainingTags, + onChangeState, +}) => { + const mutation = useIntegrationTagsMutation(); + const [needsRefetch, setNeedsRefetch] = useState(false); + + useEffect(() => { + if (needsRefetch) { + setNeedsRefetch(false); + onChangeState(); + } + }, [needsRefetch, onChangeState]); + + const handleOnAddTag = (newTag: TagModel) => { + const newTags = (tags && [...tags]) || []; + newTags.push(newTag); + mutation.mutate({ + id: id || 0, + putIntegrationTagsRequestModel: { + tags: newTags || [], + }, + }); + + setNeedsRefetch(true); + }; + + const handleOnDeleteTag = (deletedTag: TagModel) => { + const newTags = tags?.filter((tag) => tag.id !== deletedTag.id) || []; + mutation.mutate({ + id: id || 0, + putIntegrationTagsRequestModel: { + tags: newTags || [], + }, + }); + + setNeedsRefetch(true); + }; + + return ( +
+
+ +
+
+ ); +}; diff --git a/client/src/components/IntegrationItem/Name.tsx b/client/src/pages/integrations/IntegrationItem/Name.tsx similarity index 100% rename from client/src/components/IntegrationItem/Name.tsx rename to client/src/pages/integrations/IntegrationItem/Name.tsx diff --git a/client/src/components/IntegrationItem/Status.tsx b/client/src/pages/integrations/IntegrationItem/Status.tsx similarity index 100% rename from client/src/components/IntegrationItem/Status.tsx rename to client/src/pages/integrations/IntegrationItem/Status.tsx diff --git a/client/src/pages/integrations/IntegrationItem/Tag.tsx b/client/src/pages/integrations/IntegrationItem/Tag.tsx new file mode 100644 index 00000000000..69f7e0d74d5 --- /dev/null +++ b/client/src/pages/integrations/IntegrationItem/Tag.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import {Cross1Icon} from '@radix-ui/react-icons'; +import {TagModel} from '../../../data-access/integration'; + +export const Tag: React.FC<{ + tag: TagModel; + onDeleteTag: (deletedTag: TagModel) => void; +}> = ({tag, onDeleteTag}) => { + return ( + + {tag.name} + + onDeleteTag(tag)} + /> + + ); +}; diff --git a/client/src/pages/integrations/IntegrationItem/TagList.tsx b/client/src/pages/integrations/IntegrationItem/TagList.tsx new file mode 100644 index 00000000000..5d30ccde4b3 --- /dev/null +++ b/client/src/pages/integrations/IntegrationItem/TagList.tsx @@ -0,0 +1,61 @@ +import React, {useState} from 'react'; + +import {Tag} from './Tag'; +import {PlusIcon} from '@radix-ui/react-icons'; +import {TagModel} from '../../../data-access/integration'; +import CreatableSelect from 'react-select/creatable'; + +export const TagList: React.FC<{ + tags: TagModel[]; + onAddTag: (newTag: TagModel) => void; + onDeleteTag: (deletedTag: TagModel) => void; + remainingTags?: TagModel[]; +}> = ({tags, remainingTags, onAddTag, onDeleteTag}) => { + const [isNewTagWindowVisible, setIsNewTagWindowVisible] = useState(false); + + return ( +
+ {tags.map((tag) => ( + + ))} + + {!isNewTagWindowVisible ? ( +
setIsNewTagWindowVisible(true)} + > + +
+ ) : ( + ({ + label: `${tag.name + .charAt(0) + .toUpperCase()}${tag.name.slice(1)}`, + value: tag.name.toLowerCase().replace(/\W/g, ''), + ...tag, + }))} + onCreateOption={(inputValue: string) => { + onAddTag({ + name: inputValue, + }); + setIsNewTagWindowVisible(false); + }} + onChange={(selectedOption) => { + remainingTags && + onAddTag( + remainingTags.filter( + (tag) => + tag.id != null && + tag.id === selectedOption!.id + )[0] + ); + setIsNewTagWindowVisible(false); + }} + /> + )} +
+ ); +}; diff --git a/client/src/pages/integrations/IntegrationList.tsx b/client/src/pages/integrations/IntegrationList.tsx index e80aa5f3410..1f277a8fe57 100644 --- a/client/src/pages/integrations/IntegrationList.tsx +++ b/client/src/pages/integrations/IntegrationList.tsx @@ -1,6 +1,9 @@ -import {useGetIntegrationsQuery} from '../../queries/integrations.queries'; +import { + useGetIntegrationsQuery, + useGetIntegrationTagsQuery, +} from '../../queries/integrations.queries'; import React from 'react'; -import {IntegrationItem} from 'components/IntegrationItem/IntegrationItem'; +import {IntegrationItem} from 'pages/integrations/IntegrationItem/IntegrationItem'; import {Link, useSearchParams} from 'react-router-dom'; const IntegrationList: React.FC = () => { @@ -10,6 +13,7 @@ const IntegrationList: React.FC = () => { isLoading, error, data: integrations, + refetch, } = useGetIntegrationsQuery({ categoryId: searchParams.get('categoryId') ? +searchParams.get('categoryId')! @@ -19,6 +23,8 @@ const IntegrationList: React.FC = () => { : undefined, }); + const {data: tags} = useGetIntegrationTagsQuery(); + return (
    @@ -38,7 +44,7 @@ const IntegrationList: React.FC = () => { -
  • +
  • { date={integration.lastModifiedDate} status={false} // missing api button={''} + remainingTags={tags?.filter( + (tag) => + integration.tags?.findIndex( + (x) => x.id === tag.id + ) === -1 + )} + onChangeState={() => { + refetch; + }} />
  • diff --git a/client/src/queries/integrations.queries.ts b/client/src/queries/integrations.queries.ts index bcaf65e77fd..31d4713472b 100644 --- a/client/src/queries/integrations.queries.ts +++ b/client/src/queries/integrations.queries.ts @@ -39,5 +39,6 @@ export const useGetIntegrationsQuery = ( () => new IntegrationsApi().getIntegrations(requestParameters), { staleTime: 1 * 60 * 1000, + refetchInterval: 1000, } );