From 42cf28f4befa30c3d0079ce8e4725f94965a55de Mon Sep 17 00:00:00 2001 From: nzambello Date: Tue, 5 Dec 2023 09:52:59 +0200 Subject: [PATCH] feat: share qr code with tenant logo, retrieve tenant data and config from api --- src/components/Header/Header.tsx | 4 ++ src/components/MemoriWidget/MemoriWidget.tsx | 1 + .../ShareButton/ShareButton.stories.tsx | 26 ++++++- .../ShareButton/ShareButton.test.tsx | 13 ++++ src/components/ShareButton/ShareButton.tsx | 19 +++-- .../__snapshots__/ShareButton.test.tsx.snap | 70 +++++++++++++++++++ src/helpers/tenant.ts | 47 +++++++++++++ src/index.tsx | 24 ++++--- src/mocks/data.ts | 38 ++++++++-- 9 files changed, 221 insertions(+), 21 deletions(-) create mode 100644 src/helpers/tenant.ts diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx index f80a5e5f..2bf2a168 100644 --- a/src/components/Header/Header.tsx +++ b/src/components/Header/Header.tsx @@ -3,6 +3,7 @@ import cx from 'classnames'; import { Memori, Message, + Tenant, Venue, } from '@memori.ai/memori-api-client/dist/types'; import Button from '../ui/Button'; @@ -21,6 +22,7 @@ import Clear from '../icons/Clear'; export interface Props { className?: string; memori: Memori; + tenant?: Tenant; history: Message[]; position?: Venue; setShowPositionDrawer: (show: boolean) => void; @@ -39,6 +41,7 @@ export interface Props { const Header: React.FC = ({ className, memori, + tenant, history, position, setShowPositionDrawer, @@ -151,6 +154,7 @@ const Header: React.FC = ({ diff --git a/src/components/MemoriWidget/MemoriWidget.tsx b/src/components/MemoriWidget/MemoriWidget.tsx index e2987f0a..687773dc 100644 --- a/src/components/MemoriWidget/MemoriWidget.tsx +++ b/src/components/MemoriWidget/MemoriWidget.tsx @@ -2555,6 +2555,7 @@ const MemoriWidget = ({ const headerProps: HeaderProps = { memori, + tenant, history, showShare: showShare ?? integrationConfig?.showShare ?? true, position, diff --git a/src/components/ShareButton/ShareButton.stories.tsx b/src/components/ShareButton/ShareButton.stories.tsx index 80ade957..be64693b 100644 --- a/src/components/ShareButton/ShareButton.stories.tsx +++ b/src/components/ShareButton/ShareButton.stories.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { Meta, Story } from '@storybook/react'; import ShareButton, { Props } from './ShareButton'; +import { tenant } from '../../mocks/data'; import './ShareButton.css'; @@ -40,6 +41,12 @@ Default.args = { align: 'right', }; +export const Alignment = TemplateRight.bind({}); +Alignment.args = { + url: 'https://memori.ai', + align: 'left', +}; + export const WithQrCode = Template.bind({}); WithQrCode.args = { url: 'https://memori.ai', @@ -47,8 +54,21 @@ WithQrCode.args = { align: 'right', }; -export const Alignment = TemplateRight.bind({}); -Alignment.args = { +export const WithQrCodeAndTenant = Template.bind({}); +WithQrCodeAndTenant.args = { url: 'https://memori.ai', - align: 'left', + showQrCode: true, + align: 'right', + tenant: tenant, +}; + +export const WithQrCodeAndOtherTenant = Template.bind({}); +WithQrCodeAndOtherTenant.args = { + url: 'https://memori.ai', + showQrCode: true, + align: 'right', + tenant: { + ...tenant, + theme: 'tailoor', + }, }; diff --git a/src/components/ShareButton/ShareButton.test.tsx b/src/components/ShareButton/ShareButton.test.tsx index 9aa16b47..565b8af6 100644 --- a/src/components/ShareButton/ShareButton.test.tsx +++ b/src/components/ShareButton/ShareButton.test.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { render } from '@testing-library/react'; import ShareButton from './ShareButton'; +import { tenant } from '../../mocks/data'; it('renders ShareButton unchanged', () => { const { container } = render(); @@ -31,3 +32,15 @@ it('renders ShareButton aligned left unchanged', () => { const { container } = render(); expect(container).toMatchSnapshot(); }); + +it('renders ShareButton with tenant img set unchanged', () => { + const { container } = render(); + expect(container).toMatchSnapshot(); +}); + +it('renders ShareButton with other tenant img set unchanged', () => { + const { container } = render( + + ); + expect(container).toMatchSnapshot(); +}); diff --git a/src/components/ShareButton/ShareButton.tsx b/src/components/ShareButton/ShareButton.tsx index 97fd02d8..b4425e82 100644 --- a/src/components/ShareButton/ShareButton.tsx +++ b/src/components/ShareButton/ShareButton.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import Facebook from '../icons/Facebook'; import Twitter from '../icons/Twitter'; @@ -13,8 +13,10 @@ import { QRCodeCanvas } from 'qrcode.react'; import { Menu } from '@headlessui/react'; import Button from '../ui/Button'; import cx from 'classnames'; +import { Tenant } from '@memori.ai/memori-api-client/dist/types'; export interface Props { + tenant?: Tenant; url?: string; title?: string; className?: string; @@ -25,6 +27,7 @@ export interface Props { } const ShareButton: React.FC = ({ + tenant, url, title = '', className, @@ -36,6 +39,16 @@ const ShareButton: React.FC = ({ const { t } = useTranslation(); const [targetUrl, setTargetUrl] = useState(url); + const qrImageURL = useMemo( + () => + tenant?.theme + ? `${baseUrl ?? 'https://app.twincreator.com'}/images/${ + tenant.theme + }/square_logo.png` + : `${baseUrl ?? 'https://app.twincreator.com'}/images/memori_logo.png`, + [tenant, baseUrl] + ); + useEffect(() => { if (!url) setTargetUrl(window.location.href); }, [url]); @@ -167,9 +180,7 @@ const ShareButton: React.FC = ({ level={'H'} includeMargin={false} imageSettings={{ - src: `${ - baseUrl || 'https://app.twincreator.com' - }/images/memori_logo.png`, + src: qrImageURL, x: undefined, y: undefined, height: 32, diff --git a/src/components/ShareButton/__snapshots__/ShareButton.test.tsx.snap b/src/components/ShareButton/__snapshots__/ShareButton.test.tsx.snap index 45059269..ed86d33f 100644 --- a/src/components/ShareButton/__snapshots__/ShareButton.test.tsx.snap +++ b/src/components/ShareButton/__snapshots__/ShareButton.test.tsx.snap @@ -105,6 +105,76 @@ exports[`renders ShareButton with button not primary unchanged 1`] = ` `; +exports[`renders ShareButton with other tenant img set unchanged 1`] = ` +
+
+ +
+
+`; + +exports[`renders ShareButton with tenant img set unchanged 1`] = ` +
+
+ +
+
+`; + exports[`renders ShareButton with title unchanged 1`] = `
=> { + const apiBaseUrl = baseURL + ? new URL( + `${ + baseURL.startsWith('http') + ? '' + : baseURL.includes('localhost') + ? 'http://' + : 'https://' + }${baseURL}` + ).host + : 'https://app.twincreator.com'; + + try { + const response = await fetch(`${apiBaseUrl}/api/tenant/${tenantID}`); + const { tenant } = await response.json(); + + if (!tenant) { + return { + ...defaultTenant, + tenantID, + }; + } + + return tenant; + } catch (error) { + return { + ...defaultTenant, + tenantID, + }; + } +}; diff --git a/src/index.tsx b/src/index.tsx index 81496c23..74dac53c 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -3,11 +3,13 @@ import PropTypes from 'prop-types'; import { DialogState, Memori as IMemori, + Tenant, } from '@memori.ai/memori-api-client/dist/types'; import memoriApiClient from '@memori.ai/memori-api-client'; import MemoriWidget, { Props as WidgetProps, } from './components/MemoriWidget/MemoriWidget'; +import { getTenant } from './helpers/tenant'; import i18n from './i18n'; import { useTranslation } from 'react-i18next'; @@ -107,6 +109,7 @@ const Memori: React.FC = ({ userAvatar, }) => { const [memori, setMemori] = useState(); + const [tenant, setTenant] = useState(); const [speechKey, setSpeechKey] = useState( AZURE_COGNITIVE_SERVICES_TTS_KEY ); @@ -171,6 +174,17 @@ const Memori: React.FC = ({ fetchMemori(); }, [fetchMemori, tenantID]); + /** + * Fetches the Tenant data from the backend + */ + const fetchTenant = useCallback(async () => { + const tenant = await getTenant(tenantID, baseURL); + if (tenant) setTenant(tenant); + }, [tenantID, apiURL]); + useEffect(() => { + fetchTenant(); + }, [fetchTenant]); + /** * Sets the language in the i18n instance */ @@ -204,15 +218,7 @@ const Memori: React.FC = ({ }} memoriLang={spokenLang ?? memori.culture?.split('-')?.[0]} multilingual={multilingual} - tenant={{ - id: tenantID, - theme: 'twincreator', - config: { - name: tenantID, - showNewUser: false, - requirePosition: !!memori.needsPosition, - }, - }} + tenant={tenant} secret={secretToken} sessionID={sessionID} showShare={showShare} diff --git a/src/mocks/data.ts b/src/mocks/data.ts index 2fdf3da0..7464b9e8 100644 --- a/src/mocks/data.ts +++ b/src/mocks/data.ts @@ -10,14 +10,42 @@ import { export const sessionID = '131165be-9d1a-42fb-a3ce-e8f86d40c88f'; export const tenant: Tenant = { - id: 'locahost:3000', - theme: 'twincreator', + adminCount: 3, config: { - name: 'Memori', - showNewUser: true, + feedbackURL: + 'https://form.asana.com/?k=XC7S3JWQeIhnjipBuA3gbw&d=1199599736247413', + name: 'TwinCreator', requirePosition: false, - feedbackURL: 'https://form.asana.com/', + showNewUser: true, }, + creationTimestamp: '2023-05-31T14:32:48.885287Z', + description: 'TwinCreator', + disableRegistration: false, + id: 'app.twincreator.com', + lastChangeTimestamp: '2023-05-31T14:32:48.885287Z', + logoURL: 'https://app.twincreator.com/images/twincreator/logo.png', + maxAdmins: 0, + maxCompletions: 0, + maxCompletionsPerUser: 0, + maxFreeSessions: 400, + maxFreeSessionsPerUser: 100, + maxMemoriPerAdmin: 0, + maxMemoriPerUser: 3, + maxTotalMemori: 0, + maxUsers: 0, + memoriCount: 0, + name: 'app.twincreator.com', + nonFreeSessionCost: 0.02, + paying: true, + tenantID: '96caa4b4-31a4-48e5-8163-dec61869a2a7', + theme: 'twincreator', + userCount: 0, + usersCanAccessAPI: false, + usersCanCreateMemori: false, + usersCanEditDynamicIntents: false, + usersCanEditIntegrations: false, + usersCanEditMemoriChaining: false, + usersCanRunSnippets: false, }; export const memori: Memori = {