@memori.ai/memori-react 8.25.0 → 8.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/components/Header/Header.css +11 -3
  3. package/dist/components/LoginDrawer/LoginDrawer.d.ts +2 -1
  4. package/dist/components/LoginDrawer/LoginDrawer.js +2 -2
  5. package/dist/components/LoginDrawer/LoginDrawer.js.map +1 -1
  6. package/dist/components/MemoriWidget/MemoriWidget.js +74 -93
  7. package/dist/components/MemoriWidget/MemoriWidget.js.map +1 -1
  8. package/dist/components/PositionDrawer/PositionDrawer.d.ts +2 -1
  9. package/dist/components/PositionDrawer/PositionDrawer.js +3 -2
  10. package/dist/components/PositionDrawer/PositionDrawer.js.map +1 -1
  11. package/dist/components/layouts/website-assistant.css +15 -2
  12. package/dist/version.d.ts +1 -1
  13. package/dist/version.js +1 -1
  14. package/esm/components/Header/Header.css +11 -3
  15. package/esm/components/LoginDrawer/LoginDrawer.d.ts +2 -1
  16. package/esm/components/LoginDrawer/LoginDrawer.js +2 -2
  17. package/esm/components/LoginDrawer/LoginDrawer.js.map +1 -1
  18. package/esm/components/MemoriWidget/MemoriWidget.js +74 -93
  19. package/esm/components/MemoriWidget/MemoriWidget.js.map +1 -1
  20. package/esm/components/PositionDrawer/PositionDrawer.d.ts +2 -1
  21. package/esm/components/PositionDrawer/PositionDrawer.js +3 -2
  22. package/esm/components/PositionDrawer/PositionDrawer.js.map +1 -1
  23. package/esm/components/layouts/website-assistant.css +15 -2
  24. package/esm/version.d.ts +1 -1
  25. package/esm/version.js +1 -1
  26. package/package.json +2 -2
  27. package/src/components/Header/Header.css +11 -3
  28. package/src/components/LoginDrawer/LoginDrawer.tsx +4 -1
  29. package/src/components/MemoriWidget/MemoriWidget.stories.tsx +11 -0
  30. package/src/components/MemoriWidget/MemoriWidget.tsx +83 -104
  31. package/src/components/PositionDrawer/PositionDrawer.tsx +12 -2
  32. package/src/components/layouts/layouts.stories.tsx +15 -17
  33. package/src/components/layouts/website-assistant.css +15 -2
  34. package/src/version.ts +1 -1
@@ -115,6 +115,14 @@ const getMemoriState = (integrationId?: string): object | null => {
115
115
  };
116
116
  };
117
117
 
118
+ /** Place spec with all nulls for postTextEnteredEvent when position is not set or user chose "I don't want to provide my position". */
119
+ const NULL_PLACE_SPEC = {
120
+ placeName: null,
121
+ latitude: null,
122
+ longitude: null,
123
+ uncertaintyKm: null,
124
+ } as const;
125
+
118
126
  type MemoriTextEnteredEvent = CustomEvent<{
119
127
  text: string;
120
128
  waitForPrevious?: boolean;
@@ -729,31 +737,60 @@ const MemoriWidget = ({
729
737
  * Position drawer
730
738
  */
731
739
  const [position, _setPosition] = useState<Venue>();
732
- const applyPosition = async (venue?: Venue, sessionID?: string) => {
733
- const session = sessionID ?? sessionId;
734
- // Only apply position if memori.needsPosition is true
735
- if (venue && session && memori.needsPosition) {
736
- const { currentState, ...response } = await postPlaceChangedEvent({
737
- sessionId: session,
738
- placeName: venue.placeName,
739
- latitude: venue.latitude,
740
- longitude: venue.longitude,
741
- uncertaintyKm: venue.uncertainty ?? 0,
742
- });
743
740
 
744
- if (currentState && response.resultCode === 0) {
745
- _setCurrentDialogState(cds => ({
746
- ...cds,
747
- ...currentState,
748
- hints: currentState.hints?.length ? currentState.hints : cds?.hints,
749
- }));
750
- }
741
+ /** True when the user has set a real position; false when position is missing or "I don't want to provide my position". */
742
+ const hasUserProvidedPosition = useCallback((venue: Venue | undefined) => {
743
+ if (!venue) return false;
744
+ if (
745
+ venue.placeName === 'Position' &&
746
+ venue.latitude === 0 &&
747
+ venue.longitude === 0
748
+ ) {
749
+ return false;
751
750
  }
752
- };
751
+ return true;
752
+ }, []);
753
+
754
+ /** Build optional place for EnterTextSpecs (placeName and/or lat/lon; lat/lon must be together). */
755
+ const buildEnterTextPlace = useCallback((venue: Venue | undefined) => {
756
+ if (!venue) return undefined;
757
+ const place: {
758
+ placeName?: string;
759
+ latitude?: number;
760
+ longitude?: number;
761
+ uncertaintyKm?: number;
762
+ } = {};
763
+ if (
764
+ venue.latitude != null &&
765
+ venue.longitude != null
766
+ ) {
767
+ place.latitude = venue.latitude;
768
+ place.longitude = venue.longitude;
769
+ if (venue.placeName) place.placeName = venue.placeName;
770
+ if (
771
+ venue.uncertainty != null &&
772
+ venue.uncertainty > 0
773
+ )
774
+ place.uncertaintyKm = venue.uncertainty;
775
+ } else if (venue.placeName) {
776
+ place.placeName = venue.placeName;
777
+ }
778
+ return Object.keys(place).length > 0 ? place : undefined;
779
+ }, []);
780
+
781
+ /** Place to send with postTextEnteredEvent: real place, nulls when no/declined position, or undefined when position not needed. */
782
+ const getPlaceSpecForEnterText = useCallback(
783
+ (venue: Venue | undefined) => {
784
+ if (!memori.needsPosition) return undefined;
785
+ return hasUserProvidedPosition(venue)
786
+ ? buildEnterTextPlace(venue)
787
+ : NULL_PLACE_SPEC;
788
+ },
789
+ [memori.needsPosition, hasUserProvidedPosition, buildEnterTextPlace]
790
+ );
753
791
 
754
792
  const setPosition = (venue?: Venue) => {
755
793
  _setPosition(venue);
756
- applyPosition(venue);
757
794
 
758
795
  // Only save position to local config if memori.needsPosition is true
759
796
  if (venue && memori.needsPosition) {
@@ -834,10 +871,6 @@ const MemoriWidget = ({
834
871
  (window.getMemoriState() as MemoriSession)?.sessionID;
835
872
  if (!sessionID || !text?.length) return;
836
873
 
837
- if (memori.needsDateTime) {
838
- await sendDateChangedEvent({ sessionID: sessionID });
839
- }
840
-
841
874
  // Build full message text (same as what will be sent) so we can run PII check on it.
842
875
  // Order: user text -> optional translation -> appended document attachment content.
843
876
  let msg = text;
@@ -926,9 +959,14 @@ const MemoriWidget = ({
926
959
  // '"></chat-reference>';
927
960
  // }
928
961
 
962
+ const placeSpec = getPlaceSpecForEnterText(position);
929
963
  const { currentState, ...response } = await postTextEnteredEvent({
930
964
  sessionId: sessionID,
931
965
  text: msg,
966
+ ...(memori.needsDateTime && {
967
+ dateUTC: DateTime.utc().toISO() ?? undefined,
968
+ }),
969
+ ...(placeSpec !== undefined && { place: placeSpec }),
932
970
  });
933
971
  if (response.resultCode === 0 && currentState) {
934
972
  setChatLogID(undefined);
@@ -1405,16 +1443,6 @@ const MemoriWidget = ({
1405
1443
  setInstruct(false);
1406
1444
  }
1407
1445
 
1408
- if (position && memori.needsPosition)
1409
- applyPosition(position, session.sessionID);
1410
-
1411
- if (memori.needsDateTime) {
1412
- await sendDateChangedEvent({
1413
- sessionID: session.sessionID,
1414
- state: session?.currentState,
1415
- });
1416
- }
1417
-
1418
1446
  setLoading(false);
1419
1447
  return {
1420
1448
  dialogState: session.currentState,
@@ -1638,16 +1666,6 @@ const MemoriWidget = ({
1638
1666
  }
1639
1667
  }
1640
1668
 
1641
- // Apply position and date settings if needed
1642
- if (position && memori.needsPosition) {
1643
- // console.log('[REOPEN_SESSION] Applying position');
1644
- applyPosition(position, sessionID);
1645
- }
1646
- if (memori.needsDateTime) {
1647
- // console.log('[REOPEN_SESSION] Sending date changed event');
1648
- sendDateChangedEvent({ sessionID: sessionID, state: currentState });
1649
- }
1650
-
1651
1669
  setLoading(false);
1652
1670
  return {
1653
1671
  dialogState: currentState,
@@ -1707,9 +1725,14 @@ const MemoriWidget = ({
1707
1725
  pin &&
1708
1726
  (currentState.state === 'X1a' || currentState.state === 'X1b')
1709
1727
  ) {
1728
+ const placeSpec = getPlaceSpecForEnterText(position);
1710
1729
  const { resultCode: textResultCode } = await postTextEnteredEvent({
1711
1730
  sessionId,
1712
1731
  text: pin ?? '',
1732
+ ...(memori.needsDateTime && {
1733
+ dateUTC: DateTime.utc().toISO() ?? undefined,
1734
+ }),
1735
+ ...(placeSpec !== undefined && { place: placeSpec }),
1713
1736
  });
1714
1737
  textResult = textResultCode;
1715
1738
  }
@@ -1786,49 +1809,6 @@ const MemoriWidget = ({
1786
1809
  return null;
1787
1810
  };
1788
1811
 
1789
- /**
1790
- * Polling dates
1791
- */
1792
- const sendDateChangedEvent = useCallback(
1793
- async ({
1794
- sessionID,
1795
- date,
1796
- state,
1797
- }: {
1798
- sessionID?: string;
1799
- date?: string;
1800
- state?: DialogState;
1801
- }) => {
1802
- const session = sessionID ?? sessionId;
1803
- const dialogState = state ?? currentDialogState;
1804
-
1805
- if (!session || !memori.needsDateTime || dialogState?.hints?.length) {
1806
- return;
1807
- }
1808
-
1809
- const now = (date ? DateTime.fromISO(date) : DateTime.now())
1810
- .toUTC()
1811
- .toFormat('yyyy/MM/dd HH:mm:ss ZZ')
1812
- .split(':')
1813
- .slice(0, -1)
1814
- .join(':');
1815
-
1816
- const { currentState, ...response } = await postDateChangedEvent(
1817
- session,
1818
- now
1819
- );
1820
-
1821
- if (response.resultCode === 0 && currentState) {
1822
- _setCurrentDialogState(cds => ({
1823
- ...cds,
1824
- ...currentState,
1825
- hints: currentState.hints?.length ? currentState.hints : cds?.hints,
1826
- }));
1827
- }
1828
- },
1829
- [currentDialogState, memori.needsDateTime, sessionId]
1830
- );
1831
-
1832
1812
  /**
1833
1813
  * Timeout conversazione
1834
1814
  */
@@ -2527,14 +2507,6 @@ const MemoriWidget = ({
2527
2507
  // reset history
2528
2508
  setHistory([]);
2529
2509
 
2530
- // date and place events
2531
- if (position && memori.needsPosition) {
2532
- applyPosition(position, sessionID);
2533
- }
2534
- if (memori.needsDateTime) {
2535
- sendDateChangedEvent({ sessionID: sessionID, state: currentState });
2536
- }
2537
-
2538
2510
  // Handle personification tag changes
2539
2511
  if (
2540
2512
  personification &&
@@ -2698,9 +2670,14 @@ const MemoriWidget = ({
2698
2670
  setMemoriTyping(true);
2699
2671
 
2700
2672
  // we have no chat history, we start by initial question
2673
+ const placeSpec = getPlaceSpecForEnterText(position);
2701
2674
  const response = await postTextEnteredEvent({
2702
2675
  sessionId: sessionID!,
2703
2676
  text: initialQuestion,
2677
+ ...(memori.needsDateTime && {
2678
+ dateUTC: DateTime.utc().toISO() ?? undefined,
2679
+ }),
2680
+ ...(placeSpec !== undefined && { place: placeSpec }),
2704
2681
  });
2705
2682
 
2706
2683
  // Handle 500 error from TextEnteredEvent
@@ -2729,13 +2706,6 @@ const MemoriWidget = ({
2729
2706
  }
2730
2707
  }
2731
2708
 
2732
- // date and place events
2733
- if (position && memori.needsPosition) {
2734
- applyPosition(position, sessionID);
2735
- }
2736
- if (memori.needsDateTime) {
2737
- sendDateChangedEvent({ sessionID: sessionID, state: currentState });
2738
- }
2739
2709
  }
2740
2710
  // Default case - just translate and activate
2741
2711
  else {
@@ -3328,13 +3298,17 @@ const MemoriWidget = ({
3328
3298
  open={!!showPositionDrawer}
3329
3299
  venue={position}
3330
3300
  setVenue={setPosition}
3331
- onClose={position => {
3332
- if (position) applyPosition(position);
3301
+ onClose={() => {
3333
3302
  setShowPositionDrawer(false);
3334
3303
  if (autoStart) {
3335
3304
  onClickStart();
3336
3305
  }
3337
3306
  }}
3307
+ drawerClassName={
3308
+ selectedLayout === 'WEBSITE_ASSISTANT'
3309
+ ? 'memori-drawer--above-website-assistant'
3310
+ : undefined
3311
+ }
3338
3312
  />
3339
3313
  )}
3340
3314
 
@@ -3367,6 +3341,11 @@ const MemoriWidget = ({
3367
3341
  user={user}
3368
3342
  loginToken={loginToken}
3369
3343
  onClose={() => setShowLoginDrawer(false)}
3344
+ drawerClassName={
3345
+ selectedLayout === 'WEBSITE_ASSISTANT'
3346
+ ? 'memori-drawer--above-website-assistant'
3347
+ : undefined
3348
+ }
3370
3349
  onLogin={(user, token) => {
3371
3350
  //The user is logged in, so we need to set open a new session with the new token
3372
3351
  reopenSession(
@@ -2,6 +2,7 @@ import React from 'react';
2
2
  import { Memori, Venue } from '@memori.ai/memori-api-client/dist/types';
3
3
  import Drawer from '../ui/Drawer';
4
4
  import { useTranslation } from 'react-i18next';
5
+ import cx from 'classnames';
5
6
  import VenueWidget from '../VenueWidget/VenueWidget';
6
7
 
7
8
  export interface Props {
@@ -10,14 +11,23 @@ export interface Props {
10
11
  onClose: (venue?: Venue) => void;
11
12
  venue?: Venue;
12
13
  setVenue: (venue: Venue) => void;
14
+ /** Optional class for the drawer root (e.g. for z-index when layout is WEBSITE_ASSISTANT). */
15
+ drawerClassName?: string;
13
16
  }
14
17
 
15
- const PositionDrawer = ({ memori, open, onClose, venue, setVenue }: Props) => {
18
+ const PositionDrawer = ({
19
+ memori,
20
+ open,
21
+ onClose,
22
+ venue,
23
+ setVenue,
24
+ drawerClassName,
25
+ }: Props) => {
16
26
  const { t } = useTranslation();
17
27
 
18
28
  return (
19
29
  <Drawer
20
- className="memori-position-drawer"
30
+ className={cx('memori-position-drawer', drawerClassName)}
21
31
  open={open}
22
32
  onClose={() => onClose(venue)}
23
33
  title={t('widget.position') || 'Position'}
@@ -145,29 +145,27 @@ WebsiteAssistant2.args = {
145
145
  apiURL: 'https://backend.memori.ai',
146
146
  baseURL: 'https://exmachina.aclambda.online',
147
147
  uiLang: 'IT',
148
+ avatar3dHidden: true,
148
149
  spokenLang: 'IT',
149
150
  layout: 'WEBSITE_ASSISTANT',
150
151
  };
151
152
 
152
153
  export const WebsiteAssistant3 = Template.bind({});
153
154
  WebsiteAssistant3.args = {
154
- memoriName: 'FestinaLente',
155
- memoriID: 'c0ab75e2-4c56-41dc-9df6-58fa7959cda3',
156
- ownerUserID: 'cca0733c-9f24-4f1c-a3e3-99675cbb729f',
157
- tenantID: 'firenzesmart.aclambda.online',
158
- engineURL: 'https://engine.memori.ai',
159
- apiURL: 'https://backend.memori.ai',
160
- baseURL: 'https://firenzesmart.aclambda.online',
161
- uiLang: 'IT',
162
- spokenLang: 'IT',
163
- integrationID: '35a9e856-0333-4543-a428-bfe4d8025027',
164
- layout: 'WEBSITE_ASSISTANT',
165
- showSettings: false,
166
- showClear: false,
167
- showTypingText: false,
168
- showOnlyLastMessages: false,
169
- showTranslationOriginal: false,
170
- showCopyButton: false,
155
+ memoriName: "Layout Storybook",
156
+ ownerUserName: "Andrea-Patini",
157
+ memoriID: "ae20fc5a-cc15-4db9-b7dd-2cd4a621b85e",
158
+ ownerUserID: "91dbc9ba-b684-4fbe-9828-b5980af6cda9",
159
+ tenantID: "aisuru-staging.aclambda.online",
160
+ engineURL: "https://engine-staging.memori.ai/memori/v2",
161
+ apiURL: "https://backend-staging.memori.ai/api/v2",
162
+ baseURL: "http://localhost:3000",
163
+ layout: "WEBSITE_ASSISTANT",
164
+ avatar3dHidden: true,
165
+ uiLang: "IT",
166
+ spokenLang: "IT",
167
+ showOnlyLastMessages: true,
168
+ integrationID: "716f4728-919c-4015-aae1-88998a081c6f",
171
169
  };
172
170
 
173
171
 
@@ -152,6 +152,16 @@
152
152
  font-size: 14px;
153
153
  }
154
154
 
155
+ /*
156
+ * Login and Position drawers are portaled to body by Headless UI.
157
+ * When layout is WEBSITE_ASSISTANT we pass drawerClassName so they sit above the fixed panel.
158
+ */
159
+ .memori-drawer.memori-drawer--above-website-assistant,
160
+ .memori-drawer.memori-drawer--above-website-assistant .memori-drawer--backdrop,
161
+ .memori-drawer.memori-drawer--above-website-assistant .memori-drawer--container {
162
+ z-index: 2147483648;
163
+ }
164
+
155
165
  .memori-widget.memori-layout-website_assistant .memori-website_assistant--collapsed,
156
166
  .memori-widget.memori-layout-website_assistant .memori-website_assistant--expanded {
157
167
  position: fixed;
@@ -300,6 +310,7 @@
300
310
  z-index: 1000;
301
311
  display: inline-flex;
302
312
  width: auto;
313
+ max-width: 60px;
303
314
  height: auto;
304
315
  flex-direction: column;
305
316
  padding: 0.5rem;
@@ -333,9 +344,11 @@
333
344
  margin-left: 0;
334
345
  }
335
346
 
347
+ /* Position dropdown below the trigger so it does not cover the share button */
336
348
  .memori-widget.memori-layout-website_assistant .memori-share-button .memori-share-button--overlay {
337
- z-index: 1000;
338
- top: -6rem;
349
+ z-index: 1002;
350
+ top: 100%;
351
+ margin-top: 0.5rem;
339
352
  }
340
353
 
341
354
  .memori-widget.memori-layout-website_assistant .memori-header--button--fullscreen {
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  // This file is auto-generated. Do not edit manually.
2
- export const version = '8.25.0';
2
+ export const version = '8.27.0';