diff --git a/public/locales/en.json b/public/locales/en.json index bf232e2bdd..80a75b08db 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -86,6 +86,11 @@ "cancel": "Cancel", "noOrgErrorTitle": "Organizations Not Found", "noOrgErrorDescription": "Please create an organization through dashboard", + + "manageFeatures": "Manage Features", + "manageFeaturesInfo": "Creation Successful ! Please select features that you want to enale for this organization from the plugin store.", + "goToStore": "Go to Plugin Store", + "enableEverything": "Enable Everything", "noResultsFoundFor": "No results found for " }, "orgListCard": { @@ -442,7 +447,9 @@ "addOnEntry": { "enable": "Enabled", "install": "Install", - "uninstall": "Uninstall" + "uninstall": "Uninstall", + "uninstallMsg": "This feature is now removed from your organization", + "installMsg": "This feature is now enabled in your organization" }, "memberDetail": { "title": "User Details", diff --git a/src/GraphQl/Mutations/mutations.ts b/src/GraphQl/Mutations/mutations.ts index 040dea6344..550ad1e07d 100644 --- a/src/GraphQl/Mutations/mutations.ts +++ b/src/GraphQl/Mutations/mutations.ts @@ -351,8 +351,8 @@ export const REJECT_ADMIN_MUTATION = gql` * @description used to toggle `installStatus` (boolean value) of a Plugin */ export const UPDATE_INSTALL_STATUS_PLUGIN_MUTATION = gql` - mutation update_install_status_plugin_mutation($id: ID!, $orgId: ID!) { - updatePluginStatus(orgId: $orgId, id: $id) { + mutation ($id: ID!, $orgId: ID!) { + updatePluginStatus(id: $id, orgId: $orgId) { _id pluginName pluginCreatedBy diff --git a/src/GraphQl/Queries/Queries.ts b/src/GraphQl/Queries/Queries.ts index 49ac11df57..1f47769e4c 100644 --- a/src/GraphQl/Queries/Queries.ts +++ b/src/GraphQl/Queries/Queries.ts @@ -600,6 +600,7 @@ export const PLUGIN_GET = gql` pluginName pluginCreatedBy pluginDesc + uninstalledOrgs } } `; diff --git a/src/components/AddOn/core/AddOnEntry/AddOnEntry.test.tsx b/src/components/AddOn/core/AddOnEntry/AddOnEntry.test.tsx index 1716323755..50e56adf1e 100644 --- a/src/components/AddOn/core/AddOnEntry/AddOnEntry.test.tsx +++ b/src/components/AddOn/core/AddOnEntry/AddOnEntry.test.tsx @@ -23,7 +23,7 @@ const httpLink = new HttpLink({ authorization: 'Bearer ' + localStorage.getItem('token') || '', }, }); - +console.error = jest.fn(); const client: ApolloClient = new ApolloClient({ cache: new InMemoryCache(), link: ApolloLink.from([httpLink]), @@ -52,7 +52,7 @@ describe('Testing AddOnEntry', () => { - {} + {} @@ -60,4 +60,39 @@ describe('Testing AddOnEntry', () => { ); expect(getByTestId('AddOnEntry')).toBeInTheDocument(); }); + + it('renders correctly', () => { + const props = { + id: '1', + title: 'Test Addon', + description: 'Test addon description', + createdBy: 'Test User', + component: 'string', + installed: true, + configurable: true, + modified: true, + isInstalled: true, + uninstalledOrgs: [], + enabled: true, + getInstalledPlugins: (): { sample: string } => { + return { sample: 'sample' }; + }, + }; + + const { getByText } = render( + + + + + {} + + + + + ); + + expect(getByText('Test Addon')).toBeInTheDocument(); + expect(getByText('Test addon description')).toBeInTheDocument(); + expect(getByText('Test User')).toBeInTheDocument(); + }); }); diff --git a/src/components/AddOn/core/AddOnEntry/AddOnEntry.tsx b/src/components/AddOn/core/AddOnEntry/AddOnEntry.tsx index 9288c30af9..7503d0667d 100644 --- a/src/components/AddOn/core/AddOnEntry/AddOnEntry.tsx +++ b/src/components/AddOn/core/AddOnEntry/AddOnEntry.tsx @@ -1,13 +1,11 @@ import React, { useState } from 'react'; import PropTypes from 'prop-types'; import styles from './AddOnEntry.module.css'; -import { Button, Card, Form, Spinner } from 'react-bootstrap'; -import { - UPDATE_INSTALL_STATUS_PLUGIN_MUTATION, - UPDATE_ORG_STATUS_PLUGIN_MUTATION, -} from 'GraphQl/Mutations/mutations'; +import { Button, Card, Spinner } from 'react-bootstrap'; +import { UPDATE_INSTALL_STATUS_PLUGIN_MUTATION } from 'GraphQl/Mutations/mutations'; import { useMutation } from '@apollo/client'; import { useTranslation } from 'react-i18next'; +import { toast } from 'react-toastify'; interface InterfaceAddOnEntryProps { id: string; @@ -16,131 +14,52 @@ interface InterfaceAddOnEntryProps { description: string; createdBy: string; component: string; - installed?: boolean; - configurable?: boolean; modified: any; - isInstalled: boolean; + uninstalledOrgs: string[]; getInstalledPlugins: () => any; } function addOnEntry({ id, - enabled, title, description, createdBy, - installed, - isInstalled, + uninstalledOrgs, getInstalledPlugins, }: InterfaceAddOnEntryProps): JSX.Element { const { t } = useTranslation('translation', { keyPrefix: 'addOnEntry' }); - + //getting orgId from URL + const currentOrg = window.location.href.split('/id=')[1] + ''; const [buttonLoading, setButtonLoading] = useState(false); - const [switchInProgress] = useState(false); - const [isInstalledLocal, setIsInstalledLocal] = useState(isInstalled); - - const [updateInstallStatus] = useMutation( + const [isInstalledLocal, setIsInstalledLocal] = useState( + uninstalledOrgs.includes(currentOrg) + ); + // const [addOrgAsUninstalled] = useMutation(UPDATE_ORG_STATUS_PLUGIN_MUTATION); + const [addOrgAsUninstalled] = useMutation( UPDATE_INSTALL_STATUS_PLUGIN_MUTATION ); - const [updateOrgStatus] = useMutation(UPDATE_ORG_STATUS_PLUGIN_MUTATION); - - const currentOrg = window.location.href.split('=')[1]; - const updateOrgList = async (): Promise => { - await updateOrgStatus({ - variables: { - id: id.toString(), - orgId: currentOrg.toString(), - }, - }); - }; - - const updateInstallStatusFunc = async (): Promise => { + const togglePluginInstall = async (): Promise => { setButtonLoading(true); - await updateInstallStatus({ + await addOrgAsUninstalled({ variables: { id: id.toString(), - status: !isInstalledLocal, + orgId: currentOrg.toString(), }, }); setIsInstalledLocal(!isInstalledLocal); setButtonLoading(false); + const dialog: string = isInstalledLocal + ? t('installMsg') + : t('uninstallMsg'); + toast.success(dialog); }; - // useEffect(() => { - // // updateInstallStatusFunc(); - // }, []); - // TODO: Install/Remove Effect - // 1. Update Server to add to Org - // 2. Validate Permissions - // 3. Trigger Server Hook if Validated. (Stream to track progress) - // const install = () => { - // setButtonLoading(true); - // fetch('http://localhost:3005/installed', { - // method: 'POST', - // headers: { - // 'Content-type': 'application/json; charset=UTF-8', - // }, - // body: JSON.stringify( - // Object.assign( - // {}, - // { ...entry }, - // { - // installedDatetime: new Date(), - // installedBy: 'Admin', - // enabled: true, - // } - // ) - // ), - // }) - // .then(() => { - // setButtonLoading(false); - // modified(); - // }) - // .finally(() => setButtonLoading(false)); - // }; - - // const remove = () => { - // setButtonLoading(true); - // fetch(`http://localhost:3005/installed/${id}`, { - // method: 'DELETE', - // }) - // .then(() => { - // setButtonLoading(false); - // modified(); - // }) - // .finally(() => setButtonLoading(false)); - // }; - - // const toggleActive = () => { - // setSwitchState(true); - // fetch(`http://localhost:3005/installed/${id}`, { - // method: 'PUT', - // headers: { - // 'Content-type': 'application/json; charset=UTF-8', - // }, - // body: JSON.stringify( - // Object.assign( - // {}, - // { ...entry }, - // { - // enabled: !enabled, - // } - // ) - // ), - // }) - // .then(() => { - // modified(); - // setSwitchState(false); - // }) - // .finally(() => setSwitchState(false)); - // }; - return ( <> - {installed && ( + {/* {uninstalledOrgs.includes(currentOrg) && ( - )} + )} */} {title} @@ -163,22 +82,23 @@ function addOnEntry({ variant="primary" // disabled={buttonLoading || !configurable} disabled={buttonLoading} + data-testid="AddOnEntry_btn_install" onClick={(): void => { - updateOrgList(); - updateInstallStatusFunc(); + togglePluginInstall(); getInstalledPlugins(); - // installed ? remove() : install(); }} > {buttonLoading ? ( ) : ( )} {/* {installed ? 'Remove' : configurable ? 'Installed' : 'Install'} */} - {isInstalledLocal ? t('uninstall') : t('install')} + {uninstalledOrgs.includes(currentOrg) + ? t('install') + : t('uninstall')} diff --git a/src/components/AddOn/core/AddOnStore/AddOnStore.tsx b/src/components/AddOn/core/AddOnStore/AddOnStore.tsx index 1a6eaad34d..2e3e511149 100644 --- a/src/components/AddOn/core/AddOnStore/AddOnStore.tsx +++ b/src/components/AddOn/core/AddOnStore/AddOnStore.tsx @@ -240,7 +240,7 @@ function addOnStore(): JSX.Element { pluginName: string | undefined; pluginDesc: string | undefined; pluginCreatedBy: string; - pluginInstallStatus: boolean | undefined; + uninstalledOrgs: string[]; getInstalledPlugins: () => any; }, i: React.Key | null | undefined @@ -251,13 +251,14 @@ function addOnStore(): JSX.Element { title={plug.pluginName} description={plug.pluginDesc} createdBy={plug.pluginCreatedBy} - isInstalled={plug.pluginInstallStatus} - configurable={plug.pluginInstallStatus} + // isInstalled={plug.pluginInstallStatus} + // configurable={plug.pluginInstallStatus} component={'Special Component'} modified={(): void => { console.log('Plugin is modified'); }} getInstalledPlugins={getInstalledPlugins} + uninstalledOrgs={plug.uninstalledOrgs} /> ) ) @@ -319,7 +320,7 @@ function addOnStore(): JSX.Element { pluginName: string | undefined; pluginDesc: string | undefined; pluginCreatedBy: string; - pluginInstallStatus: boolean | undefined; + uninstalledOrgs: string[]; getInstalledPlugins: () => any; }, i: React.Key | null | undefined @@ -330,13 +331,14 @@ function addOnStore(): JSX.Element { title={plug.pluginName} description={plug.pluginDesc} createdBy={plug.pluginCreatedBy} - isInstalled={plug.pluginInstallStatus} - configurable={plug.pluginInstallStatus} + // isInstalled={plug.pluginInstallStatus} + // configurable={plug.pluginInstallStatus} component={'Special Component'} modified={(): void => { console.log('Plugin is modified'); }} getInstalledPlugins={getInstalledPlugins} + uninstalledOrgs={plug.uninstalledOrgs} /> ) ) @@ -356,53 +358,3 @@ addOnStore.defaultProps = {}; addOnStore.propTypes = {}; export default addOnStore; - -// {addonStore.map((plugin: any, index: number) => { -// return ( -// { -// /* istanbul ignore next */ -// pluginModified().then((installedPlugins) => { -// updateLinks( -// new PluginHelper().generateLinks(installedPlugins) -// ); -// }); -// }} -// /> -// ); -// })} - -// {installed -// .filter((plugin: any) => -// showEnabled ? plugin.enabled : !plugin.enabled -// ) -// .map((plugin: any, index: number) => { -// return ( -// { -// /* istanbul ignore next */ -// pluginModified().then((installedPlugins) => { -// updateLinks( -// new PluginHelper().generateLinks(installedPlugins) -// ); -// }); -// }} -// /> -// ); -// })} diff --git a/src/screens/OrgList/OrgList.module.css b/src/screens/OrgList/OrgList.module.css index b7ba2e1314..6e643bb80d 100644 --- a/src/screens/OrgList/OrgList.module.css +++ b/src/screens/OrgList/OrgList.module.css @@ -129,6 +129,34 @@ flex-direction: column; margin-left: 1rem; } +.titlemodaldialog { + color: #707070; + font-size: 20px; + margin-bottom: 20px; + padding-bottom: 5px; +} +form label { + font-weight: bold; + padding-bottom: 1px; + font-size: 14px; + color: #707070; +} + +form > input { + display: block; + margin-bottom: 20px; + border: 1px solid #e8e5e5; + box-shadow: 2px 1px #e8e5e5; + padding: 10px 20px; + border-radius: 5px; + background: none; + width: 100%; + transition: all 0.3s ease-in-out; + -webkit-transition: all 0.3s ease-in-out; + -moz-transition: all 0.3s ease-in-out; + -ms-transition: all 0.3s ease-in-out; + -o-transition: all 0.3s ease-in-out; +} .itemCard .loadingWrapper .innerContainer .content h5 { height: 24px; @@ -136,11 +164,62 @@ margin-bottom: 0.8rem; } +.cancel > i { + margin-top: 5px; + transform: scale(1.2); + cursor: pointer; + color: #707070; +} +.modalbody { + width: 50px; +} +.pluginStoreBtnContainer { + display: flex; + gap: 1rem; +} +.greenregbtn { + margin: 1rem 0 0; + margin-top: 10px; + border: 1px solid #e8e5e5; + box-shadow: 0 2px 2px #e8e5e5; + padding: 10px 10px; + border-radius: 5px; + background-color: #31bb6b; + width: 100%; + font-size: 16px; + color: white; + outline: none; + font-weight: 600; + cursor: pointer; + transition: transform 0.2s, box-shadow 0.2s; + width: 100%; +} + .itemCard .loadingWrapper .innerContainer .content h6[title='Location'] { display: block; width: 45%; height: 18px; } +.secondbtn { + display: flex; + align-items: center; + justify-content: center; + margin: 1rem 0 0; + margin-top: 10px; + border: 1px solid #d0cfcf; + box-shadow: 0 2px 2px #d0cfcf; + padding: 10px 10px; + border-radius: 5px; + background-color: white; + width: 100%; + font-size: 16px; + color: #31bb6b; + outline: none; + font-weight: 600; + cursor: pointer; + transition: transform 0.2s, box-shadow 0.2s; + width: 100%; +} .itemCard .loadingWrapper .innerContainer .content h6 { display: block; diff --git a/src/screens/OrgList/OrgList.tsx b/src/screens/OrgList/OrgList.tsx index 702f8b2d3c..a34a084a14 100644 --- a/src/screens/OrgList/OrgList.tsx +++ b/src/screens/OrgList/OrgList.tsx @@ -25,10 +25,27 @@ import type { } from 'utils/interfaces'; import styles from './OrgList.module.css'; import SuperAdminScreen from 'components/SuperAdminScreen/SuperAdminScreen'; +import { Link } from 'react-router-dom'; function orgList(): JSX.Element { const { t } = useTranslation('translation', { keyPrefix: 'orgList' }); + const [dialogModalisOpen, setdialogModalIsOpen] = useState(false); + /* eslint-disable @typescript-eslint/no-unused-vars */ + const [modalisOpen, setmodalIsOpen] = useState(false); + const [dialogRedirectOrgId, setDialogRedirectOrgId] = useState(''); + /* eslint-disable @typescript-eslint/explicit-function-return-type */ + function openDialogModal(redirectOrgId: string) { + setDialogRedirectOrgId(redirectOrgId); + // console.log(redirectOrgId, dialogRedirectOrgId); + setdialogModalIsOpen(true); + } + /* eslint-disable @typescript-eslint/explicit-function-return-type */ + function closeDialogModal() { + setdialogModalIsOpen(false); + } + const toggleDialogModal = (): void => + setdialogModalIsOpen(!dialogModalisOpen); document.title = t('title'); const [searchByName, setSearchByName] = useState(''); @@ -136,6 +153,7 @@ function orgList(): JSX.Element { if (data) { toast.success('Congratulation the Organization is created'); refetchOrgs(); + openDialogModal(data.createOrganization._id); setFormState({ name: '', descrip: '', @@ -437,7 +455,54 @@ function orgList(): JSX.Element { + {' '} + + +
+
+
+

{t('manageFeatures')}

+ + + +
+

+ {t('manageFeaturesInfo')} +

+ +
+ + {t('goToStore')} + + {/* */} + +
+
+
+
+ {/* Plugin Notification after Org is Created */} );