@clikvn/agent-widget-embedded 1.1.5-dev-14 → 1.1.5-dev-16
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.
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/components/Chat/Chat.js +2 -2
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/components/Chat/Chat.js.map +1 -1
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/components/Chat/Message.d.ts +3 -3
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/components/Chat/Message.d.ts.map +1 -1
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/components/Chat/Message.js +4 -3
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/components/Chat/Message.js.map +1 -1
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/components/Chat/ProductDetail.d.ts +0 -2
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/components/Chat/ProductDetail.d.ts.map +1 -1
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/components/Chat/ProductDetail.js +6 -55
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/components/Chat/ProductDetail.js.map +1 -1
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/components/Chat/ProductList.d.ts +1 -7
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/components/Chat/ProductList.d.ts.map +1 -1
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/components/Chat/ProductList.js +2 -61
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/components/Chat/ProductList.js.map +1 -1
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/components/Chat/ScenariosList.d.ts +1 -12
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/components/Chat/ScenariosList.d.ts.map +1 -1
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/components/Chat/ScenariosList.js +3 -18
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/components/Chat/ScenariosList.js.map +1 -1
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/components/Chat/Simplified/MultimodalInputSimplifiedOld.d.ts +22 -0
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/components/Chat/Simplified/MultimodalInputSimplifiedOld.d.ts.map +1 -0
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/components/Chat/Simplified/MultimodalInputSimplifiedOld.js +301 -0
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/components/Chat/Simplified/MultimodalInputSimplifiedOld.js.map +1 -0
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/constants/toolNames.d.ts +2 -9
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/constants/toolNames.d.ts.map +1 -1
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/constants/toolNames.js +2 -8
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/constants/toolNames.js.map +1 -1
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/features/AgentWidget/index.d.ts +1 -0
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/features/AgentWidget/index.d.ts.map +1 -1
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/features/AgentWidget/index.js.map +1 -1
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/hooks/useScrollToBottom.d.ts +2 -4
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/hooks/useScrollToBottom.d.ts.map +1 -1
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/hooks/useScrollToBottom.js +3 -38
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/hooks/useScrollToBottom.js.map +1 -1
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/utils/toolUtils.d.ts.map +1 -1
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/utils/toolUtils.js +15 -7
- package/.rollup.cache/Users/tungthai/Desktop/Develop/clik-ai-chatbot-embedded/dist/utils/toolUtils.js.map +1 -1
- package/dist/components/Chat/Message.d.ts +3 -3
- package/dist/components/Chat/Message.d.ts.map +1 -1
- package/dist/components/Chat/ProductDetail.d.ts +0 -2
- package/dist/components/Chat/ProductDetail.d.ts.map +1 -1
- package/dist/components/Chat/ProductList.d.ts +1 -7
- package/dist/components/Chat/ProductList.d.ts.map +1 -1
- package/dist/components/Chat/ScenariosList.d.ts +1 -12
- package/dist/components/Chat/ScenariosList.d.ts.map +1 -1
- package/dist/components/Chat/Simplified/MultimodalInputSimplifiedOld.d.ts +22 -0
- package/dist/components/Chat/Simplified/MultimodalInputSimplifiedOld.d.ts.map +1 -0
- package/dist/constants/toolNames.d.ts +2 -9
- package/dist/constants/toolNames.d.ts.map +1 -1
- package/dist/index.html +3 -3
- package/dist/utils/toolUtils.d.ts.map +1 -1
- package/dist/web.js +1 -1
- package/dist/web.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProductList.js","sourceRoot":"","sources":["../../../src/components/Chat/ProductList.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,EAAM,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AACxE,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAGxE,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE;IAC/C,KAAK,EAAE,UAAU;IACjB,QAAQ,EAAE,KAAK;CAChB,CAAC,CAAC;AAeH,MAAM,WAAW,GAAkB,CAAC,EAClC,OAAO,EACP,eAAe,EACf,QAAQ,EACR,YAAY,GAAG,IAAI,EACnB,MAAM,GACP,EAAE,EAAE;IACH,MAAM,eAAe,GAAG,MAAM,CAAyB,IAAI,CAAC,CAAC;IAC7D,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAS,EAAE,CAAC,CAAC;IACzD,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAA+B,IAAI,CAAC,CAAC;IACrE,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAU,KAAK,CAAC,CAAC;IACvD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IACxD,MAAM,EAAE,SAAS,EAAE,GAAG,WAAW,EAAE,CAAC;IACpC,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,EAAE;QAChC,IAAI,CAAC,eAAe;YAAE,OAAO,EAAE,CAAC;QAChC,OAAO,eAAe,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAC/D,CAAC,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC;IACtB,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO;QACT,CAAC;QACD,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,YAAY,MAAM,EAAE,CAAC;YAC7D,aAAa,CAAC,OAAiB,CAAC,CAAC;QACnC,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,OAAgC,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAEd,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,iBAAiB,GAAG,KAAK,IAAI,EAAE;YACnC,IAAI,CAAC,YAAY,IAAI,CAAC,YAAY,CAAC,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACvD,OAAO;YACT,CAAC;YAED,UAAU,CAAC,IAAI,CAAC,CAAC;YACjB,QAAQ,CAAC,IAAI,CAAC,CAAC;YAEf,IAAI,CAAC;gBACH,MAAM,YAAY,GAAG,MAAM,WAAW,CACpC,YAAY,EACZ,QAAQ,EACR,YAAY,EACZ,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS,CACrC,CAAC;gBACF,OAAO,CAAC,YAAY,CAAC,CAAC;YACxB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,QAAQ,CACN,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,0BAA0B,CAChE,CAAC;gBACF,OAAO,CAAC,KAAK,CAAC,0BAA0B,EAAE,GAAG,CAAC,CAAC;YACjD,CAAC;oBAAS,CAAC;gBACT,UAAU,CAAC,KAAK,CAAC,CAAC;YACpB,CAAC;QACH,CAAC,CAAC;QAEF,iBAAiB,EAAE,CAAC;IACtB,CAAC,EAAE,CAAC,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC;IACnD,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,YAAY,GAAG,eAAe,CAAC,OAAO,CAAC;QAC7C,IAAI,CAAC,UAAU,IAAI,CAAC,YAAY,EAAE,CAAC;YACjC,OAAO;QACT,CAAC;QAED,MAAM,CAAC,UAAU,CAAC;YAChB,QAAQ,EAAE,IAAI,QAAQ,EAAE;YACxB,GAAG,EAAE,IAAI;YACT,MAAM,EAAE,IAAI;YACZ,MAAM,EAAE,KAAK;YACb,QAAQ,EAAE,KAAK;YACf,QAAQ,EAAE,KAAK;YACf,UAAU,EAAE,IAAI;YAChB,WAAW,EAAE,KAAK;SACnB,CAAC,CAAC;QACH,YAAY,CAAC,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAElD,YAAY,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;YAClD,IAAI,SAAS,EAAE,CAAC,eAAe,CAAC,EAAE,CAAC;gBAChC,IAAY,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC;gBACvC,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;gBAClC,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;gBACjB,IAAI,CAAC,OAAO,GAAG,GAAG,EAAE;oBAClB,SAAS,CAAC,eAAe,CAAC,CAAE,IAAY,CAAC,WAAW,CAAC,CAAC,CAAC;gBACzD,CAAC,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC;YACzB,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;IAEjB,MAAM,aAAa,GAAG,CAAC,GAAW,EAAE,EAAE;QACpC,IAAI,SAAS,EAAE,CAAC,eAAe,CAAC,EAAE,CAAC;YACjC,SAAS,CAAC,eAAe,CAAC,CAAC,GAAG,CAAC,CAAC;QAClC,CAAC;aAAM,CAAC;YACN,MAAM,EAAE,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,iBAAiB,GAAG,CAAC,MAAc,EAAE,WAAmB,EAAE,EAAE;QAChE,IAAI,CAAC,SAAS,EAAE,CAAC,cAAc,CAAC,EAAE,CAAC;YACjC,OAAO;QACT,CAAC;QACD,uYAAuY;QACvY,IAAI,OAAO,GAAG,IAAI,CAAC;QACnB,QAAQ,MAAM,EAAE,CAAC;YACf,KAAK,cAAc;gBACjB,OAAO,GAAG;oBACR,IAAI,EAAE,mBAAmB;oBACzB,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC;wBACrB;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gCACnB,MAAM,EAAE,mBAAmB;gCAC3B,YAAY,EAAE,WAAW;6BAC1B,CAAC;yBACH;qBACF,CAAC;iBACH,CAAC;gBACF,MAAM;YACR,KAAK,iBAAiB;gBACpB,OAAO,GAAG;oBACR,IAAI,EAAE,4BAA4B;oBAClC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC;wBACrB;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gCACnB,MAAM,EAAE,4BAA4B;gCACpC,YAAY,EAAE,WAAW;6BAC1B,CAAC;yBACH;qBACF,CAAC;iBACH,CAAC;gBACF,MAAM;YACR,KAAK,aAAa;gBAChB,OAAO,GAAG;oBACR,IAAI,EAAE,wBAAwB;oBAC9B,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC;wBACrB;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gCACnB,MAAM,EAAE,wBAAwB;gCAChC,YAAY,EAAE,WAAW;6BAC1B,CAAC;yBACH;qBACF,CAAC;iBACH,CAAC;gBACF,MAAM;YACR,KAAK,YAAY;gBACf,OAAO,GAAG;oBACR,IAAI,EAAE,iBAAiB;oBACvB,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC;wBACrB;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gCACnB,MAAM,EAAE,iBAAiB;gCACzB,YAAY,EAAE,WAAW;6BAC1B,CAAC;yBACH;qBACF,CAAC;iBACH,CAAC;gBACF,MAAM;YACR;gBACE,MAAM;QACV,CAAC;QACD,IAAI,OAAO,EAAE,CAAC;YACZ,SAAS,CAAC,cAAc,CAAC,CAAC,OAAO,CAAC,CAAC;QACrC,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,gBAAgB,GAAG,OAAO,CAC9B,GAAG,EAAE,CAAC,CACJ,cAAK,SAAS,EAAC,MAAM,YACnB,cAAK,SAAS,EAAC,gDAAgD,YAC7D,eAAK,SAAS,EAAC,eAAe,aAC5B,cAAK,SAAS,EAAC,oCAAoC,GAAO,EAC1D,cAAK,SAAS,EAAC,+BAA+B,GAAO,EACrD,cAAK,SAAS,EAAC,+BAA+B,GAAO,IACjD,GACF,GACF,CACP,EACD,EAAE,CACH,CAAC;IAEF,MAAM,cAAc,GAAG,OAAO,CAC5B,GAAG,EAAE,CAAC,CACJ,cAAK,SAAS,EAAC,MAAM,YACnB,cAAK,SAAS,EAAC,6DAA6D,YACzE,KAAK,GACF,GACF,CACP,EACD,CAAC,KAAK,CAAC,CACR,CAAC;IAEF,MAAM,mBAAmB,GAAG,OAAO,CACjC,GAAG,EAAE,CAAC,CACJ,eACE,SAAS,EAAC,cAAc,EACxB,GAAG,EAAE,eAAe,EACpB,KAAK,EAAE;YACL,YAAY,EAAE,KAAK;YACnB,eAAe,EAAE,oBAAoB;YACrC,KAAK,EAAE,iBAAiB;YACxB,QAAQ,EAAE,MAAM;SACjB,GACD,CACH,EACD,CAAC,UAAU,CAAC,CACb,CAAC;IAEF,MAAM,oBAAoB,GAAG,OAAO,CAAC,GAAG,EAAE;QACxC,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC;YAAE,OAAO,IAAI,CAAC;QAC1E,OAAO,CACL,cAAK,SAAS,EAAC,MAAM,YAEnB,cACE,SAAS,EAAC,4CAA4C,EACtD,KAAK,EAAE;oBACL,OAAO,EAAE,MAAM;oBACf,GAAG,EAAE,MAAM;oBACX,OAAO,EAAE,MAAM;oBACf,aAAa,EAAE,QAAQ;oBACvB,cAAc,EAAE,QAAQ;oBACxB,UAAU,EAAE,YAAY;iBACzB,YAGD,eACE,SAAS,EAAC,sBAAsB,EAChC,KAAK,EAAE;wBACL,GAAG,EAAE,MAAM;qBACZ,aAEA,CAAC,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM;4BACvB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,CACpC,eAEE,SAAS,EAAC,kCAAkC,EAC5C,KAAK,EAAE;oCACL,GAAG,EAAE,MAAM;oCACX,SAAS,EAAE,MAAM;iCAClB,aAGD,cACE,SAAS,EAAC,mEAAmE,EAC7E,KAAK,EAAE;4CACL,eAAe,EAAE,OAAO,OAAO,CAAC,IAAI,EAAE,GAAG,GAAG;yCAC7C,GACD,EAGF,eACE,SAAS,EAAC,+CAA+C,EACzD,KAAK,EAAE;4CACL,SAAS,EAAE,MAAM;yCAClB,aAGD,cAAK,SAAS,EAAC,6CAA6C,YAC1D,aACE,SAAS,EAAC,yIAAyI,EACnJ,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE;wDACb,CAAC,CAAC,cAAc,EAAE,CAAC;wDACnB,iBAAiB,CAAC,cAAc,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;oDAClD,CAAC,YAEA,OAAO,CAAC,IAAI,GACV,GACD,EAGN,eAAK,SAAS,EAAC,6DAA6D,aAC1E,eAAK,SAAS,EAAC,SAAS,aAErB,OAAO,CAAC,qBAAqB,CAAC,MAAM,GAAG,CAAC,IAAI,CAC3C,eAAM,SAAS,EAAC,wEAAwE,YACrF,GAAG,kBAAkB,CACpB,kBAAkB,CAChB,OAAO,CAAC,qBAAqB,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,QAAQ,EAAE;oEAChD,EAAE,CACL,EACD,OAAO,CAAC,YAAY,IAAI,EAAE,EAC1B,OAAO,CAAC,IAAI,IAAI,EAAE,CACnB,GAAG,GACC,CACR,EACD,eACE,SAAS,EAAE,GAAG,OAAO,CAAC,qBAAqB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,2EAA2E,CAAC,CAAC,CAAC,+DAA+D,EAAE,YAEvM,GAAG,kBAAkB,CACpB,kBAAkB,CAAC,OAAO,EAAE,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,EACnD,OAAO,CAAC,YAAY,IAAI,EAAE,EAC1B,OAAO,CAAC,IAAI,IAAI,EAAE,CACnB,GAAG,GACC,IACH,EAEN,cACE,SAAS,EAAC,kCAAkC,EAC5C,KAAK,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,YAExC,KAAC,YAAY,IAAC,IAAI,EAAE,EAAE,EAAE,KAAK,EAAC,SAAS,GAAG,GACtC,IACF,EAGN,YACE,SAAS,EAAC,mGAAmG,EAC7G,KAAK,EAAE;oDACL,SAAS,EAAE,MAAM;oDACjB,OAAO,EAAE,aAAa;oDACtB,eAAe,EAAE,CAAC;oDAClB,eAAe,EAAE,UAAU;oDAC3B,SAAS,EAAE,YAAY;iDACxB,YAEA,OAAO,CAAC,WAAW,GAClB,IACA,KAnFD,OAAO,CAAC,IAAI,IAAI,KAAK,CAoFtB,CACP,CAAC,EAGH,CAAC,CAAC,IAAI,EAAE,WAAW,IAAI,CACtB,iBACE,SAAS,EAAC,qNAAqN,EAC/N,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE;gCACb,CAAC,CAAC,cAAc,EAAE,CAAC;gCACnB,aAAa,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;4BACnC,CAAC,kCAGM,CACV,IACG,GACF,GACF,CACP,CAAC;IACJ,CAAC,EAAE,CAAC,IAAI,EAAE,iBAAiB,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC,CAAC;IAExD,OAAO,CACL,8BACG,OAAO,IAAI,gBAAgB,EAG3B,oBAAoB,IACpB,CACJ,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC","sourcesContent":["import { Marked, Renderer } from '@ts-stack/markdown';\nimport React, { FC, useEffect, useRef, useState, useMemo } from 'react';\nimport { useChatData } from '../../hooks/useChatData';\nimport { getProducts } from '../../services/vt360.service';\nimport { BookmarkIcon } from './Icons';\nimport { convertCurrencyVnd, formatPriceProduct } from 'utils/currency';\nimport { ProductType } from 'types/product.type';\n\nconst FORMATTER = new Intl.NumberFormat('vi-VN', {\n style: 'currency',\n currency: 'VND',\n});\n\ninterface ProductDataResultType {\n products: ProductType[];\n scenarioUrl: string;\n}\n\ninterface PropTypes {\n content?: ProductDataResultType | string;\n productCodesStr?: string;\n tourCode?: string;\n languageCode?: string;\n apiUrl?: string;\n}\n\nconst ProductList: FC<PropTypes> = ({\n content,\n productCodesStr,\n tourCode,\n languageCode = 'VN',\n apiUrl,\n}) => {\n const botMessageElRef = useRef<HTMLSpanElement | null>(null);\n const [stringData, setStringData] = useState<string>('');\n const [data, setData] = useState<ProductDataResultType | null>(null);\n const [loading, setLoading] = useState<boolean>(false);\n const [error, setError] = useState<string | null>(null);\n const { listeners } = useChatData();\n const productCodes = useMemo(() => {\n if (!productCodesStr) return [];\n return productCodesStr.split(',').map((code) => code.trim());\n }, [productCodesStr]);\n useEffect(() => {\n if (!content) {\n return;\n }\n if (typeof content === 'string' || content instanceof String) {\n setStringData(content as string);\n } else {\n setData(content as ProductDataResultType);\n }\n }, [content]);\n\n useEffect(() => {\n const fetchProductsData = async () => {\n if (!productCodes || !productCodes.length || !tourCode) {\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const productsData = await getProducts(\n productCodes,\n tourCode,\n languageCode,\n apiUrl ? { api: apiUrl } : undefined\n );\n setData(productsData);\n } catch (err) {\n setError(\n err instanceof Error ? err.message : 'Failed to fetch products'\n );\n console.error('Error fetching products:', err);\n } finally {\n setLoading(false);\n }\n };\n\n fetchProductsData();\n }, [productCodes, tourCode, languageCode, apiUrl]);\n useEffect(() => {\n const botMessageEl = botMessageElRef.current;\n if (!stringData || !botMessageEl) {\n return;\n }\n\n Marked.setOptions({\n renderer: new Renderer(),\n gfm: true,\n tables: true,\n breaks: false,\n pedantic: false,\n sanitize: false,\n smartLists: true,\n smartypants: false,\n });\n botMessageEl.innerHTML = Marked.parse(stringData);\n\n botMessageEl.querySelectorAll('a').forEach((link) => {\n if (listeners?.['ON_LINK_CLICK']) {\n (link as any)['data-json'] = link.href;\n link.href = 'javascript:void(0);';\n link.target = '';\n link.onclick = () => {\n listeners['ON_LINK_CLICK']((link as any)['data-json']);\n };\n } else {\n link.target = '_blank';\n }\n });\n }, [stringData]);\n\n const handleOnClick = (url: string) => {\n if (listeners?.['ON_LINK_CLICK']) {\n listeners['ON_LINK_CLICK'](url);\n } else {\n window?.open(url, '_blank');\n }\n };\n\n const handleActionClick = (action: string, productCode: string) => {\n if (!listeners?.['CMD_CALLBACK']) {\n return;\n }\n // template command tool {\"tool\":\"show_product_image_gallery\",\"toolInput\":{\"product_code\":\"ITEM27GETN2D2M94\"},\"toolOutput\":\"[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"{\\\\n \\\\\\\"action\\\\\\\": \\\\\\\"show_product_image_gallery\\\\\\\",\\\\n \\\\\\\"product_code\\\\\\\": \\\\\\\"ITEM27GETN2D2M94\\\\\\\",\\\\n \\\\\\\"status\\\\\\\": \\\\\\\"opening\\\\\\\",\\\\n \\\\\\\"message\\\\\\\": \\\\\\\"Opening image gallery for product ITEM27GETN2D2M94\\\\\\\"\\\\n}\\\"}]\"}\n let cmdTool = null;\n switch (action) {\n case 'PRODUCT_INFO':\n cmdTool = {\n tool: 'show_product_info',\n output: JSON.stringify([\n {\n type: 'text',\n text: JSON.stringify({\n action: 'show_product_info',\n product_code: productCode,\n }),\n },\n ]),\n };\n break;\n case 'PRODUCT_GALLERY':\n cmdTool = {\n tool: 'show_product_image_gallery',\n output: JSON.stringify([\n {\n type: 'text',\n text: JSON.stringify({\n action: 'show_product_image_gallery',\n product_code: productCode,\n }),\n },\n ]),\n };\n break;\n case 'PRODUCT_360':\n cmdTool = {\n tool: 'show_product_360_image',\n output: JSON.stringify([\n {\n type: 'text',\n text: JSON.stringify({\n action: 'show_product_360_image',\n product_code: productCode,\n }),\n },\n ]),\n };\n break;\n case 'PRODUCT_AR':\n cmdTool = {\n tool: 'show_product_AR',\n output: JSON.stringify([\n {\n type: 'text',\n text: JSON.stringify({\n action: 'show_product_AR',\n product_code: productCode,\n }),\n },\n ]),\n };\n break;\n default:\n break;\n }\n if (cmdTool) {\n listeners['CMD_CALLBACK'](cmdTool);\n }\n };\n\n const loadingComponent = useMemo(\n () => (\n <div className=\"py-4\">\n <div className=\"bg-white rounded-lg border border-gray-200 p-4\">\n <div className=\"animate-pulse\">\n <div className=\"h-4 bg-gray-200 rounded w-3/4 mb-4\"></div>\n <div className=\"h-20 bg-gray-200 rounded mb-4\"></div>\n <div className=\"h-4 bg-gray-200 rounded w-1/2\"></div>\n </div>\n </div>\n </div>\n ),\n []\n );\n\n const errorComponent = useMemo(\n () => (\n <div className=\"py-4\">\n <div className=\"bg-red-50 border border-red-200 rounded-lg p-4 text-red-700\">\n {error}\n </div>\n </div>\n ),\n [error]\n );\n\n const stringDataComponent = useMemo(\n () => (\n <span\n className=\"product-list\"\n ref={botMessageElRef}\n style={{\n borderRadius: '6px',\n backgroundColor: 'rgb(255, 255, 255)',\n color: 'rgb(48, 50, 53)',\n fontSize: '16px',\n }}\n />\n ),\n [stringData]\n );\n\n const productListComponent = useMemo(() => {\n if (!data || (!data?.products?.length && !data?.scenarioUrl)) return null;\n return (\n <div className=\"py-4\">\n {/* item-card: 358px x 252px with 12px padding and 12px gap */}\n <div\n className=\"bg-white rounded-lg overflow-hidden w-full\"\n style={{\n padding: '12px',\n gap: '12px',\n display: 'flex',\n flexDirection: 'column',\n justifyContent: 'center',\n alignItems: 'flex-start',\n }}\n >\n {/* Message bubble: 334px x 228px with 12px gap */}\n <div\n className=\"flex flex-col w-full\"\n style={{\n gap: '12px',\n }}\n >\n {!!data?.products?.length &&\n data.products.map((product, index) => (\n <div\n key={product.code || index}\n className=\"flex flex-row items-start w-full\"\n style={{\n gap: '12px',\n minHeight: '82px',\n }}\n >\n {/* Image: 80px x 80px */}\n <div\n className=\"flex-shrink-0 w-[80px] h-[80px] rounded-[5px] bg-cover bg-center \"\n style={{\n backgroundImage: `url(${product.icon?.url})`,\n }}\n />\n\n {/* Content: 242px x 82px */}\n <div\n className=\"flex flex-col font-vietnam-pro flex-1 min-w-0\"\n style={{\n minHeight: '82px',\n }}\n >\n {/* Product Name */}\n <div className=\"flex flex-row items-center w-full h-5 gap-1\">\n <h3\n className=\"text-card-foreground truncate mb-[4px] font-medium text-[14px] leading-[14px] underline cursor-pointer hover:opacity-80 flex-1 min-w-0 \"\n onClick={(e) => {\n e.preventDefault();\n handleActionClick('PRODUCT_INFO', product.code);\n }}\n >\n {product.name}\n </h3>\n </div>\n\n {/* Price section */}\n <div className=\"flex flex-row gap-[4px] min-h-[28px] justify-between w-full\">\n <div className=\"min-w-0\">\n {/* Original Price (crossed out) */}\n {product.tourProductQuotations.length > 0 && (\n <span className=\"text-card-foreground text-[16px] leading-[24px] mr-[4px] font-semibold\">\n {`${formatPriceProduct(\n convertCurrencyVnd(\n product.tourProductQuotations[0]?.price.toString() ??\n ''\n ),\n product.currencyUnit ?? '',\n product.unit ?? ''\n )} `}\n </span>\n )}\n <span\n className={`${product.tourProductQuotations.length > 0 ? 'line-through text-muted-foreground text-[14px] leading-[14px] font-medium' : 'text-card-foreground text-[16px] leading-[24px] font-semibold'}`}\n >\n {`${formatPriceProduct(\n convertCurrencyVnd(product?.price.toString() ?? ''),\n product.currencyUnit ?? '',\n product.unit ?? ''\n )} `}\n </span>\n </div>\n {/* Bookmark Icon */}\n <div\n className=\"flex justify-center items-center\"\n style={{ width: '20px', height: '20px' }}\n >\n <BookmarkIcon size={20} color=\"#71717A\" />\n </div>\n </div>\n\n {/* Description */}\n <p\n className=\"font-normal text-[14px] leading-[20px] text-muted-foreground overflow-hidden w-full text-ellipsis\"\n style={{\n minHeight: '20px',\n display: '-webkit-box',\n WebkitLineClamp: 2,\n WebkitBoxOrient: 'vertical',\n wordBreak: 'break-word',\n }}\n >\n {product.description}\n </p>\n </div>\n </div>\n ))}\n\n {/* View all products button */}\n {!!data?.scenarioUrl && (\n <button\n className=\"flex h-[40px] px-[16px] py-[8px] gap-[10px] font-medium leading-[24px] flex-row justify-center items-center hover:opacity-80 transition-opacity bg-secondary border rounded-md text-sm text-card-foreground w-full\"\n onClick={(e) => {\n e.preventDefault();\n handleOnClick(data?.scenarioUrl);\n }}\n >\n View all products\n </button>\n )}\n </div>\n </div>\n </div>\n );\n }, [data, handleActionClick, handleOnClick, FORMATTER]);\n\n return (\n <>\n {loading && loadingComponent}\n {/* {error && errorComponent} */}\n {/* {!!stringData && stringDataComponent} */}\n {productListComponent}\n </>\n );\n};\n\nexport default React.memo(ProductList);\n"]}
|
|
1
|
+
{"version":3,"file":"ProductList.js","sourceRoot":"","sources":["../../../src/components/Chat/ProductList.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Marked, Renderer } from '@ts-stack/markdown';\nimport React, { FC, useEffect, useRef, useState, useMemo } from 'react';\nimport { useChatData } from '../../hooks/useChatData';\nimport { getProducts } from '../../services/vt360.service';\nimport { BookmarkIcon } from './Icons';\nimport { convertCurrencyVnd, formatPriceProduct } from 'utils/currency';\nimport { ProductType } from 'types/product.type';\n\nconst FORMATTER = new Intl.NumberFormat('vi-VN', {\n style: 'currency',\n currency: 'VND',\n});\n\ninterface ProductDataResultType {\n products: ProductType[];\n scenarioUrl: string;\n}\n\ninterface PropTypes {\n productCodes?: string[];\n tourCode?: string;\n languageCode?: string;\n apiUrl?: string;\n}\n\nconst ProductList: FC<PropTypes> = ({\n productCodes,\n tourCode,\n languageCode = 'VN',\n apiUrl,\n}) => {\n const [data, setData] = useState<ProductDataResultType | null>(null);\n const [loading, setLoading] = useState<boolean>(false);\n const { listeners } = useChatData();\n\n useEffect(() => {\n const fetchProductsData = async () => {\n if (!productCodes || !productCodes.length || !tourCode) {\n return;\n }\n\n setLoading(true);\n\n try {\n const productsData = await getProducts(\n productCodes,\n tourCode,\n languageCode,\n apiUrl ? { api: apiUrl } : undefined\n );\n setData(productsData);\n } catch (err) {\n console.error('Error fetching products:', err);\n } finally {\n setLoading(false);\n }\n };\n\n fetchProductsData();\n }, [productCodes, tourCode, languageCode, apiUrl]);\n\n const handleOnClick = (url: string) => {\n if (listeners?.['ON_LINK_CLICK']) {\n listeners['ON_LINK_CLICK'](url);\n } else {\n window?.open(url, '_blank');\n }\n };\n\n const handleActionClick = (action: string, productCode: string) => {\n if (!listeners?.['CMD_CALLBACK']) {\n return;\n }\n // template command tool {\"tool\":\"show_product_image_gallery\",\"toolInput\":{\"product_code\":\"ITEM27GETN2D2M94\"},\"toolOutput\":\"[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"{\\\\n \\\\\\\"action\\\\\\\": \\\\\\\"show_product_image_gallery\\\\\\\",\\\\n \\\\\\\"product_code\\\\\\\": \\\\\\\"ITEM27GETN2D2M94\\\\\\\",\\\\n \\\\\\\"status\\\\\\\": \\\\\\\"opening\\\\\\\",\\\\n \\\\\\\"message\\\\\\\": \\\\\\\"Opening image gallery for product ITEM27GETN2D2M94\\\\\\\"\\\\n}\\\"}]\"}\n let cmdTool = null;\n switch (action) {\n case 'PRODUCT_INFO':\n cmdTool = {\n tool: 'show_product_info',\n output: JSON.stringify([\n {\n type: 'text',\n text: JSON.stringify({\n action: 'show_product_info',\n product_code: productCode,\n }),\n },\n ]),\n };\n break;\n case 'PRODUCT_GALLERY':\n cmdTool = {\n tool: 'show_product_image_gallery',\n output: JSON.stringify([\n {\n type: 'text',\n text: JSON.stringify({\n action: 'show_product_image_gallery',\n product_code: productCode,\n }),\n },\n ]),\n };\n break;\n case 'PRODUCT_360':\n cmdTool = {\n tool: 'show_product_360_image',\n output: JSON.stringify([\n {\n type: 'text',\n text: JSON.stringify({\n action: 'show_product_360_image',\n product_code: productCode,\n }),\n },\n ]),\n };\n break;\n case 'PRODUCT_AR':\n cmdTool = {\n tool: 'show_product_AR',\n output: JSON.stringify([\n {\n type: 'text',\n text: JSON.stringify({\n action: 'show_product_AR',\n product_code: productCode,\n }),\n },\n ]),\n };\n break;\n default:\n break;\n }\n if (cmdTool) {\n listeners['CMD_CALLBACK'](cmdTool);\n }\n };\n\n const loadingComponent = useMemo(\n () => (\n <div className=\"py-4\">\n <div className=\"bg-white rounded-lg border border-gray-200 p-4\">\n <div className=\"animate-pulse\">\n <div className=\"h-4 bg-gray-200 rounded w-3/4 mb-4\"></div>\n <div className=\"h-20 bg-gray-200 rounded mb-4\"></div>\n <div className=\"h-4 bg-gray-200 rounded w-1/2\"></div>\n </div>\n </div>\n </div>\n ),\n []\n );\n\n const productListComponent = useMemo(() => {\n if (!data || (!data?.products?.length && !data?.scenarioUrl)) return null;\n return (\n <div className=\"py-4\">\n {/* item-card: 358px x 252px with 12px padding and 12px gap */}\n <div\n className=\"bg-white rounded-lg overflow-hidden w-full\"\n style={{\n padding: '12px',\n gap: '12px',\n display: 'flex',\n flexDirection: 'column',\n justifyContent: 'center',\n alignItems: 'flex-start',\n }}\n >\n {/* Message bubble: 334px x 228px with 12px gap */}\n <div\n className=\"flex flex-col w-full\"\n style={{\n gap: '12px',\n }}\n >\n {!!data?.products?.length &&\n data.products.map((product, index) => (\n <div\n key={product.code || index}\n className=\"flex flex-row items-start w-full\"\n style={{\n gap: '12px',\n minHeight: '82px',\n }}\n >\n {/* Image: 80px x 80px */}\n <div\n className=\"flex-shrink-0 w-[80px] h-[80px] rounded-[5px] bg-cover bg-center \"\n style={{\n backgroundImage: `url(${product.icon?.url})`,\n }}\n />\n\n {/* Content: 242px x 82px */}\n <div\n className=\"flex flex-col font-vietnam-pro flex-1 min-w-0\"\n style={{\n minHeight: '82px',\n }}\n >\n {/* Product Name */}\n <div className=\"flex flex-row items-center w-full h-5 gap-1\">\n <h3\n className=\"text-card-foreground truncate mb-[4px] font-medium text-[14px] leading-[14px] underline cursor-pointer hover:opacity-80 flex-1 min-w-0 \"\n onClick={(e) => {\n e.preventDefault();\n handleActionClick('PRODUCT_INFO', product.code);\n }}\n >\n {product.name}\n </h3>\n </div>\n\n {/* Price section */}\n <div className=\"flex flex-row gap-[4px] min-h-[28px] justify-between w-full\">\n <div className=\"min-w-0\">\n {/* Original Price (crossed out) */}\n {product.tourProductQuotations.length > 0 && (\n <span className=\"text-card-foreground text-[16px] leading-[24px] mr-[4px] font-semibold\">\n {`${formatPriceProduct(\n convertCurrencyVnd(\n product.tourProductQuotations[0]?.price.toString() ??\n ''\n ),\n product.currencyUnit ?? '',\n product.unit ?? ''\n )} `}\n </span>\n )}\n <span\n className={`${product.tourProductQuotations.length > 0 ? 'line-through text-muted-foreground text-[14px] leading-[14px] font-medium' : 'text-card-foreground text-[16px] leading-[24px] font-semibold'}`}\n >\n {`${formatPriceProduct(\n convertCurrencyVnd(product?.price.toString() ?? ''),\n product.currencyUnit ?? '',\n product.unit ?? ''\n )} `}\n </span>\n </div>\n {/* Bookmark Icon */}\n <div\n className=\"flex justify-center items-center\"\n style={{ width: '20px', height: '20px' }}\n >\n <BookmarkIcon size={20} color=\"#71717A\" />\n </div>\n </div>\n\n {/* Description */}\n <p\n className=\"font-normal text-[14px] leading-[20px] text-muted-foreground overflow-hidden w-full text-ellipsis\"\n style={{\n minHeight: '20px',\n display: '-webkit-box',\n WebkitLineClamp: 2,\n WebkitBoxOrient: 'vertical',\n wordBreak: 'break-word',\n }}\n >\n {product.description}\n </p>\n </div>\n </div>\n ))}\n\n {/* View all products button */}\n {!!data?.scenarioUrl && (\n <button\n className=\"flex h-[40px] px-[16px] py-[8px] gap-[10px] font-medium leading-[24px] flex-row justify-center items-center hover:opacity-80 transition-opacity bg-secondary border rounded-md text-sm text-card-foreground w-full\"\n onClick={(e) => {\n e.preventDefault();\n handleOnClick(data?.scenarioUrl);\n }}\n >\n View all products\n </button>\n )}\n </div>\n </div>\n </div>\n );\n }, [data, handleActionClick, handleOnClick, FORMATTER]);\n\n return (\n <>\n {loading && loadingComponent}\n {productListComponent}\n </>\n );\n};\n\nexport default React.memo(ProductList);\n"]}
|
|
@@ -1,17 +1,6 @@
|
|
|
1
1
|
import { FC } from 'react';
|
|
2
|
-
interface ScenarioType {
|
|
3
|
-
code: string;
|
|
4
|
-
name: string;
|
|
5
|
-
description?: string;
|
|
6
|
-
image?: {
|
|
7
|
-
file: {
|
|
8
|
-
url: string;
|
|
9
|
-
};
|
|
10
|
-
};
|
|
11
|
-
}
|
|
12
2
|
interface PropTypes {
|
|
13
|
-
|
|
14
|
-
scenarioCodesStr?: string;
|
|
3
|
+
scenarioCodes?: string[];
|
|
15
4
|
tourCode?: string;
|
|
16
5
|
languageCode?: string;
|
|
17
6
|
apiUrl?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScenariosList.d.ts","sourceRoot":"","sources":["../../../src/components/Chat/ScenariosList.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAgC,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"ScenariosList.d.ts","sourceRoot":"","sources":["../../../src/components/Chat/ScenariosList.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAgC,MAAM,OAAO,CAAC;AAezD,UAAU,SAAS;IACjB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,eAAO,MAAM,aAAa,EAAE,EAAE,CAAC,SAAS,CAoKvC,CAAC"}
|
|
@@ -2,36 +2,22 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { useEffect, useState, useMemo } from 'react';
|
|
3
3
|
import { useChatData } from '../../hooks/useChatData';
|
|
4
4
|
import { getScenarios } from '../../services/vt360.service';
|
|
5
|
-
export const ScenariosList = ({
|
|
5
|
+
export const ScenariosList = ({ scenarioCodes, tourCode, languageCode = 'VN', apiUrl, playBtn = false, }) => {
|
|
6
6
|
const [data, setData] = useState(null);
|
|
7
7
|
const [showAll, setShowAll] = useState(false);
|
|
8
8
|
const [loading, setLoading] = useState(false);
|
|
9
|
-
const [error, setError] = useState(null);
|
|
10
9
|
const { listeners } = useChatData();
|
|
11
|
-
const scenarioCodes = useMemo(() => {
|
|
12
|
-
if (!scenarioCodesStr)
|
|
13
|
-
return [];
|
|
14
|
-
return scenarioCodesStr.split(',').map((code) => code.trim());
|
|
15
|
-
}, [scenarioCodesStr]);
|
|
16
|
-
useEffect(() => {
|
|
17
|
-
if (!content) {
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
setData(content);
|
|
21
|
-
}, [content]);
|
|
22
10
|
useEffect(() => {
|
|
23
11
|
const fetchScenariosData = async () => {
|
|
24
12
|
if (!scenarioCodes || !scenarioCodes.length || !tourCode) {
|
|
25
13
|
return;
|
|
26
14
|
}
|
|
27
15
|
setLoading(true);
|
|
28
|
-
setError(null);
|
|
29
16
|
try {
|
|
30
17
|
const scenariosData = await getScenarios(scenarioCodes, tourCode, languageCode, apiUrl ? { api: apiUrl } : undefined);
|
|
31
18
|
setData(scenariosData);
|
|
32
19
|
}
|
|
33
20
|
catch (err) {
|
|
34
|
-
setError(err instanceof Error ? err.message : 'Failed to fetch scenarios');
|
|
35
21
|
console.error('Error fetching scenarios:', err);
|
|
36
22
|
}
|
|
37
23
|
finally {
|
|
@@ -39,7 +25,7 @@ export const ScenariosList = ({ content, scenarioCodesStr, tourCode, languageCod
|
|
|
39
25
|
}
|
|
40
26
|
};
|
|
41
27
|
fetchScenariosData();
|
|
42
|
-
}, [scenarioCodes
|
|
28
|
+
}, [scenarioCodes]);
|
|
43
29
|
const handleOnClick = (url) => {
|
|
44
30
|
if (listeners?.['ON_LINK_CLICK']) {
|
|
45
31
|
listeners['ON_LINK_CLICK'](url);
|
|
@@ -49,7 +35,6 @@ export const ScenariosList = ({ content, scenarioCodesStr, tourCode, languageCod
|
|
|
49
35
|
}
|
|
50
36
|
};
|
|
51
37
|
const loadingComponent = useMemo(() => (_jsx("div", { className: "py-4", children: _jsx("div", { className: "bg-white rounded-lg border border-gray-200 p-4", children: _jsxs("div", { className: "animate-pulse", children: [_jsx("div", { className: "h-4 bg-gray-200 rounded w-3/4 mb-4" }), _jsx("div", { className: "h-20 bg-gray-200 rounded mb-4" }), _jsx("div", { className: "h-4 bg-gray-200 rounded w-1/2" })] }) }) })), []);
|
|
52
|
-
const errorComponent = useMemo(() => (_jsx("div", { className: "py-4", children: _jsx("div", { className: "bg-red-50 border border-red-200 rounded-lg p-4 text-red-700", children: error }) })), [error]);
|
|
53
38
|
const scenarioListComponent = useMemo(() => {
|
|
54
39
|
if (!data || !data.length)
|
|
55
40
|
return null;
|
|
@@ -81,6 +66,6 @@ export const ScenariosList = ({ content, scenarioCodesStr, tourCode, languageCod
|
|
|
81
66
|
wordBreak: 'break-word',
|
|
82
67
|
}, children: scenario.description }))] })] }, scenario.code || index))), !showAll && data?.length > 5 && (_jsx("button", { className: "flex h-[40px] px-[16px] py-[8px] gap-[10px] font-medium leading-[24px] flex-row justify-center items-center hover:opacity-80 transition-opacity bg-secondary border rounded-md text-sm text-card-foreground w-full", onClick: () => setShowAll(true), children: "View all scenarios" }))] }) }) }));
|
|
83
68
|
}, [data, showAll, handleOnClick]);
|
|
84
|
-
return (_jsxs(_Fragment, { children: [loading && loadingComponent,
|
|
69
|
+
return (_jsxs(_Fragment, { children: [loading && loadingComponent, scenarioListComponent] }));
|
|
85
70
|
};
|
|
86
71
|
//# sourceMappingURL=ScenariosList.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScenariosList.js","sourceRoot":"","sources":["../../../src/components/Chat/ScenariosList.tsx"],"names":[],"mappings":"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","sourcesContent":["import { FC, useEffect, useState, useMemo } from 'react';\nimport { useChatData } from '../../hooks/useChatData';\nimport { getScenarios } from '../../services/vt360.service';\n\ninterface ScenarioType {\n code: string;\n name: string;\n description?: string;\n image?: {\n file: {\n url: string;\n };\n };\n}\n\ninterface PropTypes {\n content?: ScenarioType[];\n scenarioCodesStr?: string;\n tourCode?: string;\n languageCode?: string;\n apiUrl?: string;\n playBtn?: boolean;\n}\n\nexport const ScenariosList: FC<PropTypes> = ({\n content,\n scenarioCodesStr,\n tourCode,\n languageCode = 'VN',\n apiUrl,\n playBtn = false,\n}) => {\n const [data, setData] = useState<ScenarioType[] | null>(null);\n const [showAll, setShowAll] = useState<boolean>(false);\n const [loading, setLoading] = useState<boolean>(false);\n const [error, setError] = useState<string | null>(null);\n const { listeners } = useChatData();\n\n const scenarioCodes = useMemo(() => {\n if (!scenarioCodesStr) return [];\n return scenarioCodesStr.split(',').map((code) => code.trim());\n }, [scenarioCodesStr]);\n\n useEffect(() => {\n if (!content) {\n return;\n }\n setData(content);\n }, [content]);\n\n useEffect(() => {\n const fetchScenariosData = async () => {\n if (!scenarioCodes || !scenarioCodes.length || !tourCode) {\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const scenariosData = await getScenarios(\n scenarioCodes,\n tourCode,\n languageCode,\n apiUrl ? { api: apiUrl } : undefined\n );\n setData(scenariosData);\n } catch (err) {\n setError(\n err instanceof Error ? err.message : 'Failed to fetch scenarios'\n );\n console.error('Error fetching scenarios:', err);\n } finally {\n setLoading(false);\n }\n };\n\n fetchScenariosData();\n }, [scenarioCodes, tourCode, languageCode, apiUrl]);\n\n const handleOnClick = (url: string) => {\n if (listeners?.['ON_LINK_CLICK']) {\n listeners['ON_LINK_CLICK'](url);\n } else {\n window?.open(url, '_blank');\n }\n };\n\n const loadingComponent = useMemo(\n () => (\n <div className=\"py-4\">\n <div className=\"bg-white rounded-lg border border-gray-200 p-4\">\n <div className=\"animate-pulse\">\n <div className=\"h-4 bg-gray-200 rounded w-3/4 mb-4\"></div>\n <div className=\"h-20 bg-gray-200 rounded mb-4\"></div>\n <div className=\"h-4 bg-gray-200 rounded w-1/2\"></div>\n </div>\n </div>\n </div>\n ),\n []\n );\n\n const errorComponent = useMemo(\n () => (\n <div className=\"py-4\">\n <div className=\"bg-red-50 border border-red-200 rounded-lg p-4 text-red-700\">\n {error}\n </div>\n </div>\n ),\n [error]\n );\n\n const scenarioListComponent = useMemo(() => {\n if (!data || !data.length) return null;\n\n return (\n <div className=\"py-4\">\n <div\n className=\"bg-white rounded-lg overflow-hidden w-full\"\n style={{\n padding: '12px',\n gap: '12px',\n display: 'flex',\n flexDirection: 'column',\n justifyContent: 'center',\n alignItems: 'flex-start',\n }}\n >\n <div\n className=\"flex flex-col w-full\"\n style={{\n gap: '12px',\n }}\n >\n {[...data]\n .splice(0, showAll ? data.length : 5)\n .map((scenario, index) => (\n <div\n key={scenario.code || index}\n className=\"flex flex-row items-start w-full\"\n style={{\n gap: '12px',\n minHeight: '82px',\n }}\n >\n <div\n className=\"flex-shrink-0 w-[80px] h-[80px] rounded-[5px] bg-cover bg-center bg-gray-200\"\n style={{\n backgroundImage: scenario.image?.file?.url\n ? `url(${scenario.image.file.url})`\n : 'none',\n }}\n />\n <div\n className=\"flex flex-col items-center justify-center font-vietnam-pro flex-1 min-w-0\"\n style={{\n minHeight: '82px',\n }}\n >\n <div className=\"flex flex-row items-center w-full h-5 gap-1\">\n <h3\n className=\"text-card-foreground truncate mb-[4px] font-medium text-[14px] leading-[14px] underline cursor-pointer hover:opacity-80 flex-1 min-w-0\"\n onClick={() => handleOnClick(scenario.code)}\n >\n {scenario.name}\n </h3>\n </div>\n {playBtn && (\n <div className=\"flex flex-row gap-[4px] min-h-[28px] justify-end w-full\">\n <button\n className=\"flex h-[28px] px-[12px] py-[4px] gap-[8px] font-medium leading-[20px] flex-row justify-center items-center hover:opacity-80 transition-opacity bg-secondary border rounded-md text-sm text-card-foreground\"\n onClick={() => handleOnClick(scenario.code)}\n >\n Play\n </button>\n </div>\n )}\n {scenario.description && (\n <p\n className=\"font-normal text-[14px] leading-[20px] text-muted-foreground overflow-hidden w-full text-ellipsis\"\n style={{\n minHeight: '20px',\n display: '-webkit-box',\n WebkitLineClamp: 3,\n WebkitBoxOrient: 'vertical',\n wordBreak: 'break-word',\n }}\n >\n {scenario.description}\n </p>\n )}\n </div>\n </div>\n ))}\n\n {!showAll && data?.length > 5 && (\n <button\n className=\"flex h-[40px] px-[16px] py-[8px] gap-[10px] font-medium leading-[24px] flex-row justify-center items-center hover:opacity-80 transition-opacity bg-secondary border rounded-md text-sm text-card-foreground w-full\"\n onClick={() => setShowAll(true)}\n >\n View all scenarios\n </button>\n )}\n </div>\n </div>\n </div>\n );\n }, [data, showAll, handleOnClick]);\n\n return (\n <>\n {loading && loadingComponent}\n {error && errorComponent}\n {scenarioListComponent}\n </>\n );\n};\n"]}
|
|
1
|
+
{"version":3,"file":"ScenariosList.js","sourceRoot":"","sources":["../../../src/components/Chat/ScenariosList.tsx"],"names":[],"mappings":"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","sourcesContent":["import { FC, useEffect, useState, useMemo } from 'react';\nimport { useChatData } from '../../hooks/useChatData';\nimport { getScenarios } from '../../services/vt360.service';\n\ninterface ScenarioType {\n code: string;\n name: string;\n description?: string;\n image?: {\n file: {\n url: string;\n };\n };\n}\n\ninterface PropTypes {\n scenarioCodes?: string[];\n tourCode?: string;\n languageCode?: string;\n apiUrl?: string;\n playBtn?: boolean;\n}\n\nexport const ScenariosList: FC<PropTypes> = ({\n scenarioCodes,\n tourCode,\n languageCode = 'VN',\n apiUrl,\n playBtn = false,\n}) => {\n const [data, setData] = useState<ScenarioType[] | null>(null);\n const [showAll, setShowAll] = useState<boolean>(false);\n const [loading, setLoading] = useState<boolean>(false);\n const { listeners } = useChatData();\n\n useEffect(() => {\n const fetchScenariosData = async () => {\n if (!scenarioCodes || !scenarioCodes.length || !tourCode) {\n return;\n }\n\n setLoading(true);\n\n try {\n const scenariosData = await getScenarios(\n scenarioCodes,\n tourCode,\n languageCode,\n apiUrl ? { api: apiUrl } : undefined\n );\n setData(scenariosData);\n } catch (err) {\n console.error('Error fetching scenarios:', err);\n } finally {\n setLoading(false);\n }\n };\n\n fetchScenariosData();\n }, [scenarioCodes]);\n\n const handleOnClick = (url: string) => {\n if (listeners?.['ON_LINK_CLICK']) {\n listeners['ON_LINK_CLICK'](url);\n } else {\n window?.open(url, '_blank');\n }\n };\n\n const loadingComponent = useMemo(\n () => (\n <div className=\"py-4\">\n <div className=\"bg-white rounded-lg border border-gray-200 p-4\">\n <div className=\"animate-pulse\">\n <div className=\"h-4 bg-gray-200 rounded w-3/4 mb-4\"></div>\n <div className=\"h-20 bg-gray-200 rounded mb-4\"></div>\n <div className=\"h-4 bg-gray-200 rounded w-1/2\"></div>\n </div>\n </div>\n </div>\n ),\n []\n );\n\n const scenarioListComponent = useMemo(() => {\n if (!data || !data.length) return null;\n\n return (\n <div className=\"py-4\">\n <div\n className=\"bg-white rounded-lg overflow-hidden w-full\"\n style={{\n padding: '12px',\n gap: '12px',\n display: 'flex',\n flexDirection: 'column',\n justifyContent: 'center',\n alignItems: 'flex-start',\n }}\n >\n <div\n className=\"flex flex-col w-full\"\n style={{\n gap: '12px',\n }}\n >\n {[...data]\n .splice(0, showAll ? data.length : 5)\n .map((scenario, index) => (\n <div\n key={scenario.code || index}\n className=\"flex flex-row items-start w-full\"\n style={{\n gap: '12px',\n minHeight: '82px',\n }}\n >\n <div\n className=\"flex-shrink-0 w-[80px] h-[80px] rounded-[5px] bg-cover bg-center bg-gray-200\"\n style={{\n backgroundImage: scenario.image?.file?.url\n ? `url(${scenario.image.file.url})`\n : 'none',\n }}\n />\n <div\n className=\"flex flex-col items-center justify-center font-vietnam-pro flex-1 min-w-0\"\n style={{\n minHeight: '82px',\n }}\n >\n <div className=\"flex flex-row items-center w-full h-5 gap-1\">\n <h3\n className=\"text-card-foreground truncate mb-[4px] font-medium text-[14px] leading-[14px] underline cursor-pointer hover:opacity-80 flex-1 min-w-0\"\n onClick={() => handleOnClick(scenario.code)}\n >\n {scenario.name}\n </h3>\n </div>\n {playBtn && (\n <div className=\"flex flex-row gap-[4px] min-h-[28px] justify-end w-full\">\n <button\n className=\"flex h-[28px] px-[12px] py-[4px] gap-[8px] font-medium leading-[20px] flex-row justify-center items-center hover:opacity-80 transition-opacity bg-secondary border rounded-md text-sm text-card-foreground\"\n onClick={() => handleOnClick(scenario.code)}\n >\n Play\n </button>\n </div>\n )}\n {scenario.description && (\n <p\n className=\"font-normal text-[14px] leading-[20px] text-muted-foreground overflow-hidden w-full text-ellipsis\"\n style={{\n minHeight: '20px',\n display: '-webkit-box',\n WebkitLineClamp: 3,\n WebkitBoxOrient: 'vertical',\n wordBreak: 'break-word',\n }}\n >\n {scenario.description}\n </p>\n )}\n </div>\n </div>\n ))}\n\n {!showAll && data?.length > 5 && (\n <button\n className=\"flex h-[40px] px-[16px] py-[8px] gap-[10px] font-medium leading-[24px] flex-row justify-center items-center hover:opacity-80 transition-opacity bg-secondary border rounded-md text-sm text-card-foreground w-full\"\n onClick={() => setShowAll(true)}\n >\n View all scenarios\n </button>\n )}\n </div>\n </div>\n </div>\n );\n }, [data, showAll, handleOnClick]);\n\n return (\n <>\n {loading && loadingComponent}\n {scenarioListComponent}\n </>\n );\n};\n"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
import { BotType } from '../../../types/bot.type';
|
|
3
|
+
import { ChatMessageType, IFileUpload } from '../../../types/flowise.type';
|
|
4
|
+
type PropsType = {
|
|
5
|
+
input: string;
|
|
6
|
+
setInput: (value: string) => void;
|
|
7
|
+
loadingChat: boolean;
|
|
8
|
+
message: ChatMessageType | null;
|
|
9
|
+
chatId: string;
|
|
10
|
+
handleSubmit: (event?: {
|
|
11
|
+
preventDefault?: () => void;
|
|
12
|
+
}, files?: IFileUpload[]) => void;
|
|
13
|
+
className?: string;
|
|
14
|
+
bot: BotType | null;
|
|
15
|
+
apiHost: string;
|
|
16
|
+
setEnableTTS: (value: boolean) => void;
|
|
17
|
+
enableTTS: boolean;
|
|
18
|
+
onAudioEnded: () => void;
|
|
19
|
+
};
|
|
20
|
+
export declare const MultimodalInputSimplified: FC<PropsType>;
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=MultimodalInputSimplifiedOld.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MultimodalInputSimplifiedOld.d.ts","sourceRoot":"","sources":["../../../../src/components/Chat/Simplified/MultimodalInputSimplifiedOld.tsx"],"names":[],"mappings":"AACA,OAAO,EAEL,EAAE,EAKH,MAAM,OAAO,CAAC;AAMf,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAe3E,KAAK,SAAS,GAAG;IACf,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,eAAe,GAAG,IAAI,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,CACZ,KAAK,CAAC,EAAE;QAAE,cAAc,CAAC,EAAE,MAAM,IAAI,CAAA;KAAE,EACvC,KAAK,CAAC,EAAE,WAAW,EAAE,KAClB,IAAI,CAAC;IACV,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,OAAO,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,MAAM,IAAI,CAAC;CAC1B,CAAC;AAcF,eAAO,MAAM,yBAAyB,EAAE,EAAE,CAAC,SAAS,CA0iBnD,CAAC"}
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { AnimatePresence, motion } from 'framer-motion';
|
|
3
|
+
import { useCallback, useEffect, useRef, useState, } from 'react';
|
|
4
|
+
import { useWindowSize } from 'usehooks-ts';
|
|
5
|
+
import { useAudioRecording } from '../../../hooks/useAudioRecording';
|
|
6
|
+
import { useChatData } from '../../../hooks/useChatData';
|
|
7
|
+
import { useConfiguration } from '../../../hooks/useConfiguration';
|
|
8
|
+
import { getAudioTranscript } from '../../../services/chat.service';
|
|
9
|
+
import { cn, generateUUID, sleep } from '../../../utils/commonUtils';
|
|
10
|
+
import { getBestMimeType, getFileExtension } from '../../../utils/fileUtils';
|
|
11
|
+
import { AnimatedRecordingIcon, ArrowUpIcon, CheckCirclFillIcon, ClikAIIcon, ClikChatIcon, ClikCloseIcon, ClikMicrophoneIcon, } from '../Icons';
|
|
12
|
+
import { Button } from '../ui/Button';
|
|
13
|
+
import AudioPlayerSimplified from './AudioPlayerSimplified';
|
|
14
|
+
export const MultimodalInputSimplified = (props) => {
|
|
15
|
+
const { input, setInput, loadingChat, message, chatId, handleSubmit, className, bot, apiHost, setEnableTTS, onAudioEnded, } = props;
|
|
16
|
+
const { onLoaded, theme = {} } = useConfiguration();
|
|
17
|
+
const { modeButtonChat = false, gapInput = 16 } = theme;
|
|
18
|
+
const { listeners } = useChatData();
|
|
19
|
+
const { isRecording, onRecordingCancelled, onRecordingStopped, onRecordingStarted, isLoadingRecording, audioData, } = useAudioRecording();
|
|
20
|
+
const { width } = useWindowSize();
|
|
21
|
+
const language = theme?.language?.options
|
|
22
|
+
?.find((option) => option.code === theme?.language?.code)
|
|
23
|
+
?.name?.toUpperCase() || 'EN';
|
|
24
|
+
const [transcribing, setTranscribing] = useState(false);
|
|
25
|
+
const [showAudioPlayer, setShowAudioPlayer] = useState(false);
|
|
26
|
+
const [submitting, setSubmitting] = useState(false);
|
|
27
|
+
const [expanded, setExpanded] = useState(!modeButtonChat);
|
|
28
|
+
const [animationCompleted, setAnimationCompleted] = useState(false);
|
|
29
|
+
const textareaRef = useRef(null);
|
|
30
|
+
const setInputRef = useRef(null);
|
|
31
|
+
const welcomeMessage = theme.welcomeMessage || "Hi, I'm your showroom assistant";
|
|
32
|
+
const adjustHeight = () => {
|
|
33
|
+
if (textareaRef.current) {
|
|
34
|
+
textareaRef.current.style.height = '24px';
|
|
35
|
+
textareaRef.current.style.height =
|
|
36
|
+
`${textareaRef.current?.scrollHeight}px`;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
const readonly = isRecording || transcribing;
|
|
40
|
+
const handleSetInput = useCallback((value) => {
|
|
41
|
+
setInput(value);
|
|
42
|
+
}, [setInput]);
|
|
43
|
+
const handleInput = (event) => {
|
|
44
|
+
handleSetInput(event.target.value);
|
|
45
|
+
if (textareaRef.current) {
|
|
46
|
+
textareaRef.current.scrollTop = textareaRef.current.scrollHeight;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
const submitForm = useCallback(async () => {
|
|
50
|
+
setSubmitting(true);
|
|
51
|
+
if (listeners?.['ON_SUBMIT_MESSAGE']) {
|
|
52
|
+
listeners['ON_SUBMIT_MESSAGE']();
|
|
53
|
+
}
|
|
54
|
+
handleSubmit(undefined);
|
|
55
|
+
await sleep(500);
|
|
56
|
+
setShowAudioPlayer(true);
|
|
57
|
+
}, [handleSubmit, listeners]);
|
|
58
|
+
const toAudioBase64 = (blob) => {
|
|
59
|
+
return new Promise((resolve) => {
|
|
60
|
+
let mimeType = getBestMimeType();
|
|
61
|
+
const pos = blob.type.indexOf(';');
|
|
62
|
+
if (pos === -1) {
|
|
63
|
+
mimeType = blob.type;
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
mimeType = blob.type.substring(0, pos);
|
|
67
|
+
}
|
|
68
|
+
// read blob and add to previews
|
|
69
|
+
const reader = new FileReader();
|
|
70
|
+
reader.readAsDataURL(blob);
|
|
71
|
+
reader.onloadend = () => {
|
|
72
|
+
const base64data = reader.result;
|
|
73
|
+
const upload = {
|
|
74
|
+
tempId: generateUUID(),
|
|
75
|
+
data: base64data,
|
|
76
|
+
type: 'audio',
|
|
77
|
+
name: `audio_${Date.now()}.${getFileExtension(mimeType)}`,
|
|
78
|
+
mime: mimeType,
|
|
79
|
+
};
|
|
80
|
+
resolve(upload);
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
const speechToText = async (blob) => {
|
|
85
|
+
try {
|
|
86
|
+
setTranscribing(true);
|
|
87
|
+
const fileUpload = await toAudioBase64(blob);
|
|
88
|
+
const base64Data = fileUpload.data.replace(/^data:.+;base64,/, '');
|
|
89
|
+
const byteCharacters = atob(base64Data); // Decode Base64 string
|
|
90
|
+
const byteNumbers = new Array(byteCharacters.length);
|
|
91
|
+
for (let i = 0; i < byteCharacters.length; i++) {
|
|
92
|
+
byteNumbers[i] = byteCharacters.charCodeAt(i);
|
|
93
|
+
}
|
|
94
|
+
const byteArray = new Uint8Array(byteNumbers);
|
|
95
|
+
const file = new File([byteArray], fileUpload.name, {
|
|
96
|
+
lastModified: new Date().getTime(),
|
|
97
|
+
type: fileUpload.type,
|
|
98
|
+
});
|
|
99
|
+
const text = await getAudioTranscript({ file, apiHost, language });
|
|
100
|
+
if (text) {
|
|
101
|
+
handleSetInput(text);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
console.error('Error getting audio transcript!', error);
|
|
106
|
+
}
|
|
107
|
+
finally {
|
|
108
|
+
setTranscribing(false);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
const handleFinishRecording = () => {
|
|
112
|
+
if (isRecording) {
|
|
113
|
+
onRecordingStopped(speechToText);
|
|
114
|
+
}
|
|
115
|
+
if (listeners?.['ON_FINISHED_RECORDING']) {
|
|
116
|
+
listeners['ON_FINISHED_RECORDING']();
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
const handleStartRecording = useCallback(() => {
|
|
120
|
+
onRecordingStarted();
|
|
121
|
+
handleSetInput('');
|
|
122
|
+
if (listeners?.['ON_START_RECORDING']) {
|
|
123
|
+
listeners['ON_START_RECORDING']();
|
|
124
|
+
}
|
|
125
|
+
}, [onRecordingStarted, handleSetInput, listeners]);
|
|
126
|
+
const handleCancelRecording = () => {
|
|
127
|
+
onRecordingCancelled();
|
|
128
|
+
handleSetInput('');
|
|
129
|
+
if (listeners?.['ON_CANCEL_RECORDING']) {
|
|
130
|
+
listeners['ON_CANCEL_RECORDING']();
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
const handleSend = useCallback(async () => {
|
|
134
|
+
if (!isRecording) {
|
|
135
|
+
submitForm();
|
|
136
|
+
}
|
|
137
|
+
}, [submitForm, isRecording]);
|
|
138
|
+
const handleOnClickOpenChatmode = () => {
|
|
139
|
+
handleOnClick('WelcomeMsg');
|
|
140
|
+
};
|
|
141
|
+
const handleOnClick = (type) => {
|
|
142
|
+
if (listeners?.['ON_CLICK']) {
|
|
143
|
+
listeners['ON_CLICK'](type);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
const handleToggleExpand = () => {
|
|
147
|
+
handleOnClick('ClickButtonAI');
|
|
148
|
+
setExpanded(!expanded);
|
|
149
|
+
setAnimationCompleted(false);
|
|
150
|
+
if (!expanded) {
|
|
151
|
+
// Focus vào textarea khi expand
|
|
152
|
+
setTimeout(() => {
|
|
153
|
+
if (textareaRef.current) {
|
|
154
|
+
textareaRef.current.focus();
|
|
155
|
+
}
|
|
156
|
+
}, 300);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
useEffect(() => {
|
|
160
|
+
adjustHeight();
|
|
161
|
+
}, [input, readonly]);
|
|
162
|
+
useEffect(() => {
|
|
163
|
+
setInputRef.current = setInput;
|
|
164
|
+
}, [setInput]);
|
|
165
|
+
useEffect(() => {
|
|
166
|
+
if (onLoaded) {
|
|
167
|
+
onLoaded({
|
|
168
|
+
setInput: (txt) => {
|
|
169
|
+
if (setInputRef.current) {
|
|
170
|
+
setInputRef.current(txt || '');
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
startRecording: async () => {
|
|
174
|
+
handleStartRecording();
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}, []);
|
|
179
|
+
useEffect(() => {
|
|
180
|
+
setEnableTTS(true);
|
|
181
|
+
return () => {
|
|
182
|
+
setEnableTTS(false);
|
|
183
|
+
};
|
|
184
|
+
}, [setEnableTTS]);
|
|
185
|
+
useEffect(() => {
|
|
186
|
+
if (message && submitting) {
|
|
187
|
+
setSubmitting(false);
|
|
188
|
+
if (listeners?.['ON_FINISHED_SUBMITTING_MESSAGE']) {
|
|
189
|
+
listeners['ON_FINISHED_SUBMITTING_MESSAGE']();
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}, [message, submitting, listeners]);
|
|
193
|
+
const recordingPlaceholder = isRecording && !transcribing
|
|
194
|
+
? theme?.input?.recordingPlaceholder || 'Recording...'
|
|
195
|
+
: '';
|
|
196
|
+
const transcribingPlaceholder = transcribing
|
|
197
|
+
? theme?.input?.transcribingPlaceholder || 'Transcribing...'
|
|
198
|
+
: '';
|
|
199
|
+
const processing = isRecording || transcribing;
|
|
200
|
+
const canShowAudioPlayer = !theme?.noAutoplayMessage &&
|
|
201
|
+
showAudioPlayer &&
|
|
202
|
+
message?.new &&
|
|
203
|
+
message?.tts;
|
|
204
|
+
// Animation variants
|
|
205
|
+
const containerVariants = {
|
|
206
|
+
collapsed: {
|
|
207
|
+
width: '0px',
|
|
208
|
+
height: '0px',
|
|
209
|
+
opacity: 0,
|
|
210
|
+
borderRadius: '28px',
|
|
211
|
+
},
|
|
212
|
+
expanded: {
|
|
213
|
+
width: width ? `${width - gapInput}px` : '400px',
|
|
214
|
+
height: 'auto',
|
|
215
|
+
opacity: 1,
|
|
216
|
+
borderRadius: '16px',
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
const contentVariants = {
|
|
220
|
+
collapsed: {
|
|
221
|
+
opacity: 0,
|
|
222
|
+
scale: 0.8,
|
|
223
|
+
},
|
|
224
|
+
expanded: {
|
|
225
|
+
opacity: 1,
|
|
226
|
+
scale: 1,
|
|
227
|
+
},
|
|
228
|
+
};
|
|
229
|
+
const showButtonAI = !isRecording &&
|
|
230
|
+
modeButtonChat &&
|
|
231
|
+
!input?.length &&
|
|
232
|
+
!processing &&
|
|
233
|
+
!submitting &&
|
|
234
|
+
!transcribing;
|
|
235
|
+
const showToggleChatIcon = showButtonAI && expanded;
|
|
236
|
+
let centerWidthOffset = 72; // left: 24px, right: 32px, gap: 8px x 2, padding: 12px
|
|
237
|
+
if (transcribing || submitting) {
|
|
238
|
+
centerWidthOffset = 80; // left: 24px, right: 40px, gap: 8px x 2, padding: 12px
|
|
239
|
+
}
|
|
240
|
+
if (showToggleChatIcon) {
|
|
241
|
+
centerWidthOffset = 108; // left: 24px, right: 68px, gap: 8px x 2, padding: 12px
|
|
242
|
+
}
|
|
243
|
+
return (_jsxs("div", { className: "flex justify-end", children: [showButtonAI && (_jsx("div", { className: cn('absolute z-20 transition-all duration-300 top-1 right-1'), children: _jsx(Button, { className: cn('rounded-full bg-[#fff] hover:bg-[#fff] text-white border-none shadow-lg w-[40px] h-[40px] p-0 flex items-center justify-center', expanded && 'bg-[#595959] hover:bg-[#595959] text-white'), onClick: handleToggleExpand, children: expanded ? (_jsx(ClikChatIcon, { size: 24, style: { width: '24px', height: '24px' } })) : (_jsx(ClikAIIcon, { size: 24, style: { width: '24px', height: '24px' } })) }) })), _jsxs(motion.div, { variants: containerVariants, initial: "collapsed", animate: expanded ? 'expanded' : 'collapsed', transition: { duration: 0.3, ease: 'easeInOut' }, className: cn('relative flex gap-4 bg-[#ffffff] py-[8px] px-[12px] scrollbar-hidden border border-[#E5E5E5] shadow-[0px_2px_6px_0px_rgba(0,0,0,0.08)] backdrop-blur-[50px]', className, {
|
|
244
|
+
'flex-col min-h-[48px]': !canShowAudioPlayer && expanded,
|
|
245
|
+
'flex-row min-h-[48px]': canShowAudioPlayer && expanded,
|
|
246
|
+
'items-center justify-center': !expanded,
|
|
247
|
+
}), children: [_jsx(AnimatePresence, { children: expanded && (_jsx(motion.div, { variants: contentVariants, initial: "collapsed", animate: "expanded", exit: "collapsed", transition: { duration: 0.2, delay: 0 }, onAnimationComplete: () => setAnimationCompleted(true), className: `w-full ${showButtonAI ? 'pr-9' : ''}`, children: !canShowAudioPlayer && (_jsxs(_Fragment, { children: [!processing && !!input?.length && (_jsx("textarea", { readOnly: readonly, ref: textareaRef, value: input, onChange: handleInput, placeholder: "Nh\u1EADp tin nh\u1EAFn c\u1EE7a b\u1EA1n...", className: cn(`resize-none text-base bg-muted bg-[#ffffff] scrollbar-hidden ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 w-full mb-3`), style: {
|
|
248
|
+
fontSize: '16px',
|
|
249
|
+
}, onKeyDown: (event) => {
|
|
250
|
+
if (isRecording) {
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
if (event.key === 'Enter' && !event.shiftKey) {
|
|
254
|
+
event.preventDefault();
|
|
255
|
+
if (loadingChat) {
|
|
256
|
+
console.error('Please wait for the model to finish its response!');
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
handleSend();
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
} })), processing && (_jsx("div", { className: cn(`placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 resize-none text-base scrollbar-hidden mb-3`, className), style: {
|
|
263
|
+
fontSize: '16px',
|
|
264
|
+
}, children: _jsxs("div", { className: "animate-pulse", children: [recordingPlaceholder, transcribingPlaceholder] }) })), _jsxs("div", { className: "flex flex-row gap-[8px] items-center justify-between", children: [_jsxs("div", { className: "w-[24px] h-[24px]", children: [!isRecording && (_jsx("button", { className: "w-[24px] h-[24px] opacity-100", onClick: (e) => {
|
|
265
|
+
e.preventDefault();
|
|
266
|
+
e.stopPropagation();
|
|
267
|
+
handleOnClick('ChangeLanguage');
|
|
268
|
+
}, "aria-label": "Change language", children: _jsx("span", { className: "text-[#595959] text-center text-[12px] border-[1px] font-bold rounded-[4px] border-[#595959] px-[2px]", children: language }) })), isRecording && (_jsx(Button, { disabled: isLoadingRecording, className: "rounded-full dark:border-zinc-700 h-[24px] w-[24px] px-0 py-0", variant: "outline", onClick: (event) => {
|
|
269
|
+
event.preventDefault();
|
|
270
|
+
handleOnClick('CancelRecording');
|
|
271
|
+
handleCancelRecording();
|
|
272
|
+
}, children: _jsx(ClikCloseIcon, { className: "!w-full !h-full" }) }))] }), _jsxs("div", { className: "min-h-[24px] flex items-center justify-center text-ellipsis overflow-hidden whitespace-nowrap", style: { width: `calc(100% - ${centerWidthOffset}px)` }, children: [!isRecording &&
|
|
273
|
+
!input?.length &&
|
|
274
|
+
!loadingChat &&
|
|
275
|
+
!transcribing &&
|
|
276
|
+
animationCompleted && (_jsx("div", { className: "text-center text-[14px] text-muted-foreground cursor-pointer w-full text-ellipsis overflow-hidden whitespace-nowrap", onClick: handleOnClickOpenChatmode, children: welcomeMessage })), isRecording && (_jsx(AnimatedRecordingIcon, { frequencies: audioData?.frequencies, isActive: true }))] }), !input?.length && (_jsxs("div", { className: "relative", style: transcribing || submitting
|
|
277
|
+
? { width: '40px', height: '40px', padding: '4px' }
|
|
278
|
+
: undefined, children: [(transcribing || submitting) && (_jsx("div", { className: "absolute rounded-full left-0 top-0 w-[40px] h-[40px] animate-spin z-[1]", style: {
|
|
279
|
+
background: 'conic-gradient(from 180deg at 50% 50%, #1E6EB4 0deg, rgba(30, 110, 180, 0) 360deg)',
|
|
280
|
+
} })), (!isRecording || transcribing) && (_jsxs(Button, { className: "relative rounded-full z-[2] w-[32px] h-[32px] m-0 text-[#18181B] disabled:opacity-100", onClick: (event) => {
|
|
281
|
+
event.preventDefault();
|
|
282
|
+
handleOnClick('StartRecording');
|
|
283
|
+
handleStartRecording();
|
|
284
|
+
}, variant: "outline", size: "icon", disabled: loadingChat || isRecording || submitting, children: [!submitting && _jsx(ClikMicrophoneIcon, { size: 20 }), submitting && _jsx(ArrowUpIcon, { size: 20 })] }))] })), !input?.length && isRecording && !isLoadingRecording && (_jsx(Button, { className: "rounded-full dark:border-zinc-700 p-0 w-[32px] h-[32px] border-none flex-shrink-0", variant: "outline", onClick: (event) => {
|
|
285
|
+
event.preventDefault();
|
|
286
|
+
handleOnClick('FinishRecording');
|
|
287
|
+
handleFinishRecording();
|
|
288
|
+
}, children: _jsx(CheckCirclFillIcon, { className: "!w-full !h-full" }) })), !!input?.length && (_jsxs("div", { className: "relative flex gap-[4px]", children: [_jsx(Button, { className: "rounded-full z-[2] p-1.5 w-[32px] h-[32px] m-0 px-2 py-1 text-[#18181B]", onClick: (event) => {
|
|
289
|
+
event.preventDefault();
|
|
290
|
+
handleOnClick('StartRecording');
|
|
291
|
+
handleStartRecording();
|
|
292
|
+
}, variant: "outline", disabled: loadingChat, children: _jsx(ClikMicrophoneIcon, { size: 16 }) }), _jsx(Button, { className: "rounded-full z-[2] p-1.5 w-[32px] h-[32px] m-0 bg-zinc-900 hover:bg-zinc-900 px-2 py-1 text-[#FFF]", onClick: (event) => {
|
|
293
|
+
event.preventDefault();
|
|
294
|
+
handleOnClick('Submit');
|
|
295
|
+
handleSend();
|
|
296
|
+
}, disabled: isRecording || !input?.length || loadingChat, children: _jsx(ArrowUpIcon, { size: 16 }) })] }))] })] })) })) }), canShowAudioPlayer && expanded && (_jsx(motion.div, { variants: contentVariants, initial: "collapsed", animate: "expanded", exit: "collapsed", transition: { duration: 0.2, delay: 0 }, className: "w-full", children: _jsx(AudioPlayerSimplified, { message: message, chatId: chatId, autoplay: true, onClose: () => {
|
|
297
|
+
setShowAudioPlayer(false);
|
|
298
|
+
onAudioEnded();
|
|
299
|
+
} }) }))] })] }));
|
|
300
|
+
};
|
|
301
|
+
//# sourceMappingURL=MultimodalInputSimplifiedOld.js.map
|