From 3da97d97bec4000b79f68d55f01aec55d7456ce9 Mon Sep 17 00:00:00 2001 From: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Date: Fri, 17 Jan 2025 14:34:44 +0530 Subject: [PATCH 001/105] fix: Download button opens URL instead of downloading (#10710) --- .../components-next/message/bubbles/Image.vue | 14 ++++---------- .../components-next/message/chips/Audio.vue | 14 +++----------- .../conversation/components/GalleryView.vue | 8 +++----- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 5 files changed, 16 insertions(+), 32 deletions(-) diff --git a/app/javascript/dashboard/components-next/message/bubbles/Image.vue b/app/javascript/dashboard/components-next/message/bubbles/Image.vue index bb5aba572..ce05071ee 100644 --- a/app/javascript/dashboard/components-next/message/bubbles/Image.vue +++ b/app/javascript/dashboard/components-next/message/bubbles/Image.vue @@ -5,6 +5,8 @@ import Button from 'next/button/Button.vue'; import Icon from 'next/icon/Icon.vue'; import { useSnakeCase } from 'dashboard/composables/useTransformKeys'; import { useMessageContext } from '../provider.js'; +import { downloadFile } from '@chatwoot/utils'; + import GalleryView from 'dashboard/components/widgets/conversation/components/GalleryView.vue'; const emit = defineEmits(['error']); @@ -23,16 +25,8 @@ const handleError = () => { }; const downloadAttachment = async () => { - const response = await fetch(attachment.value.dataUrl); - const blob = await response.blob(); - const url = window.URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = `attachment${attachment.value.extension || ''}`; - document.body.appendChild(a); - a.click(); - window.URL.revokeObjectURL(url); - document.body.removeChild(a); + const { fileType, dataUrl, extension } = attachment.value; + downloadFile({ url: dataUrl, type: fileType, extension }); }; diff --git a/app/javascript/dashboard/components-next/message/chips/Audio.vue b/app/javascript/dashboard/components-next/message/chips/Audio.vue index eb5093d29..95c0ba1aa 100644 --- a/app/javascript/dashboard/components-next/message/chips/Audio.vue +++ b/app/javascript/dashboard/components-next/message/chips/Audio.vue @@ -2,6 +2,7 @@ import { computed, onMounted, useTemplateRef, ref } from 'vue'; import Icon from 'next/icon/Icon.vue'; import { timeStampAppendedURL } from 'dashboard/helper/URLHelper'; +import { downloadFile } from '@chatwoot/utils'; const { attachment } = defineProps({ attachment: { @@ -73,17 +74,8 @@ const onEnd = () => { }; const downloadAudio = async () => { - const response = await fetch(timeStampURL.value); - const blob = await response.blob(); - const url = window.URL.createObjectURL(blob); - const anchor = document.createElement('a'); - anchor.href = url; - const filename = timeStampURL.value.split('/').pop().split('?')[0] || 'audio'; - anchor.download = filename; - document.body.appendChild(anchor); - anchor.click(); - window.URL.revokeObjectURL(url); - document.body.removeChild(anchor); + const { fileType, dataUrl, extension } = attachment; + downloadFile({ url: dataUrl, type: fileType, extension }); }; diff --git a/app/javascript/dashboard/components/widgets/conversation/components/GalleryView.vue b/app/javascript/dashboard/components/widgets/conversation/components/GalleryView.vue index ffdc71cf2..200984f4c 100644 --- a/app/javascript/dashboard/components/widgets/conversation/components/GalleryView.vue +++ b/app/javascript/dashboard/components/widgets/conversation/components/GalleryView.vue @@ -3,6 +3,7 @@ import { ref, computed, onMounted } from 'vue'; import { useStoreGetters } from 'dashboard/composables/store'; import { useKeyboardEvents } from 'dashboard/composables/useKeyboardEvents'; import { messageTimestamp } from 'shared/helpers/timeHelper'; +import { downloadFile } from '@chatwoot/utils'; import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue'; @@ -117,14 +118,11 @@ const onClickChangeAttachment = (attachment, index) => { }; const onClickDownload = () => { - const { file_type: type, data_url: url } = activeAttachment.value; + const { file_type: type, data_url: url, extension } = activeAttachment.value; if (!Object.values(ALLOWED_FILE_TYPES).includes(type)) { return; } - const link = document.createElement('a'); - link.href = url; - link.download = `attachment.${type}`; - link.click(); + downloadFile({ url, type, extension }); }; const onRotate = type => { diff --git a/package.json b/package.json index 7858b3dcb..83bfb4bf5 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "@breezystack/lamejs": "^1.2.7", "@chatwoot/ninja-keys": "1.2.3", "@chatwoot/prosemirror-schema": "1.1.1-next", - "@chatwoot/utils": "^0.0.30", + "@chatwoot/utils": "^0.0.31", "@formkit/core": "^1.6.7", "@formkit/vue": "^1.6.7", "@hcaptcha/vue3-hcaptcha": "^1.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c0dde98c7..590f16924 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,8 +23,8 @@ importers: specifier: 1.1.1-next version: 1.1.1-next '@chatwoot/utils': - specifier: ^0.0.30 - version: 0.0.30 + specifier: ^0.0.31 + version: 0.0.31 '@formkit/core': specifier: ^1.6.7 version: 1.6.7 @@ -404,8 +404,8 @@ packages: '@chatwoot/prosemirror-schema@1.1.1-next': resolution: {integrity: sha512-/M2qZ+ZF7GlQNt1riwVP499fvp3hxSqd5iy8hxyF9pkj9qQ+OKYn5JK+v3qwwqQY3IxhmNOn1Lp6tm7vstrd9Q==} - '@chatwoot/utils@0.0.30': - resolution: {integrity: sha512-UfKn2GUV/9PF7zoj17dAoyx5RZkJihjjclhWTtG0SHRfRizKS/pg0SpXSt9ToAEDeNeRtqmD7RrVMUaso8TZxw==} + '@chatwoot/utils@0.0.31': + resolution: {integrity: sha512-15ir4Jf6q4/gIFC6KqgWZu/NEWYnaw5j2/JB+CYGzZ5a/e+rXG7nmY/sObvRqCnMW6LCS2++shlVQdTxle3CyQ==} engines: {node: '>=10'} '@codemirror/commands@6.7.0': @@ -5154,7 +5154,7 @@ snapshots: prosemirror-utils: 1.2.2(prosemirror-model@1.22.3)(prosemirror-state@1.4.3) prosemirror-view: 1.34.1 - '@chatwoot/utils@0.0.30': + '@chatwoot/utils@0.0.31': dependencies: date-fns: 2.29.3 From 5046dc572798e6f0508ecdae244ce1d5f050bb3f Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 17 Jan 2025 15:30:52 +0530 Subject: [PATCH 002/105] feat: add CSAT & Form bubble (#10711) ![CleanShot 2025-01-17 at 14 02 40@2x](https://github.com/user-attachments/assets/7a19c4d7-7548-4b6a-92bb-0ddba181c428) ![CleanShot 2025-01-17 at 14 39 50@2x](https://github.com/user-attachments/assets/4251c944-2d1f-4cb5-817a-6352a644b743) --------- Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> --- .../components-next/message/Message.vue | 12 ++++ .../components-next/message/bubbles/CSAT.vue | 45 ++++++++++++++ .../components-next/message/bubbles/Form.vue | 62 +++++++++++++++++++ .../i18n/locale/en/conversation.json | 1 + 4 files changed, 120 insertions(+) create mode 100644 app/javascript/dashboard/components-next/message/bubbles/CSAT.vue create mode 100644 app/javascript/dashboard/components-next/message/bubbles/Form.vue diff --git a/app/javascript/dashboard/components-next/message/Message.vue b/app/javascript/dashboard/components-next/message/Message.vue index 78b637e65..df0602a49 100644 --- a/app/javascript/dashboard/components-next/message/Message.vue +++ b/app/javascript/dashboard/components-next/message/Message.vue @@ -34,6 +34,8 @@ import UnsupportedBubble from './bubbles/Unsupported.vue'; import ContactBubble from './bubbles/Contact.vue'; import DyteBubble from './bubbles/Dyte.vue'; import LocationBubble from './bubbles/Location.vue'; +import CSATBubble from './bubbles/CSAT.vue'; +import FormBubble from './bubbles/Form.vue'; import MessageError from './MessageError.vue'; import ContextMenu from 'dashboard/modules/conversations/components/MessageContextMenu.vue'; @@ -260,6 +262,16 @@ const componentToRender = computed(() => { if (emailInboxTypes.includes(props.messageType)) return EmailBubble; } + if (props.contentType === CONTENT_TYPES.INPUT_CSAT) { + return CSATBubble; + } + + if ( + [CONTENT_TYPES.INPUT_SELECT, CONTENT_TYPES.FORM].includes(props.contentType) + ) { + return FormBubble; + } + if (props.contentType === CONTENT_TYPES.INCOMING_EMAIL) { return EmailBubble; } diff --git a/app/javascript/dashboard/components-next/message/bubbles/CSAT.vue b/app/javascript/dashboard/components-next/message/bubbles/CSAT.vue new file mode 100644 index 000000000..211840944 --- /dev/null +++ b/app/javascript/dashboard/components-next/message/bubbles/CSAT.vue @@ -0,0 +1,45 @@ + + + diff --git a/app/javascript/dashboard/components-next/message/bubbles/Form.vue b/app/javascript/dashboard/components-next/message/bubbles/Form.vue new file mode 100644 index 000000000..0bf92a07b --- /dev/null +++ b/app/javascript/dashboard/components-next/message/bubbles/Form.vue @@ -0,0 +1,62 @@ + + + diff --git a/app/javascript/dashboard/i18n/locale/en/conversation.json b/app/javascript/dashboard/i18n/locale/en/conversation.json index 6e17875b0..994bc8609 100644 --- a/app/javascript/dashboard/i18n/locale/en/conversation.json +++ b/app/javascript/dashboard/i18n/locale/en/conversation.json @@ -54,6 +54,7 @@ "SUCCESS_DELETE_MESSAGE": "Message deleted successfully", "FAIL_DELETE_MESSSAGE": "Couldn't delete message! Try again", "NO_RESPONSE": "No response", + "RESPONSE": "Response", "RATING_TITLE": "Rating", "FEEDBACK_TITLE": "Feedback", "REPLY_MESSAGE_NOT_FOUND": "Message not available", From c25a75f497e2a9f81354091be6ddf296cb73a2f9 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Sat, 18 Jan 2025 18:31:07 +0530 Subject: [PATCH 003/105] feat: check if item is present (#10715) --- .../dashboard/components-next/message/bubbles/Form.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/app/javascript/dashboard/components-next/message/bubbles/Form.vue b/app/javascript/dashboard/components-next/message/bubbles/Form.vue index 0bf92a07b..ca9af4994 100644 --- a/app/javascript/dashboard/components-next/message/bubbles/Form.vue +++ b/app/javascript/dashboard/components-next/message/bubbles/Form.vue @@ -30,6 +30,7 @@ const formValues = computed(() => { if (contentType.value === CONTENT_TYPES.INPUT_SELECT) { const [item] = contentAttributes.value?.submittedValues ?? []; + if (!item) return []; return [ { From a8ecbd391953eca489b67ce03d7cbb2d32c1c23b Mon Sep 17 00:00:00 2001 From: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Date: Mon, 20 Jan 2025 14:34:23 +0530 Subject: [PATCH 004/105] chore: Auto-fetch previous page on last item deletion (#10714) # Pull Request Template ## Description This PR fixes an issue where deleting the last item on the last page of responses/documents, would show an empty state instead of loading the previous page. Fixes > If you have pending responses spanning 2 or more pages .. and you delete the last response in the last page.. instead of showing the previous page the system show empty state. ## Type of change - [x] Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? **Loom video** https://www.loom.com/share/b0e89f774ccd45dab0e8dba2c34bd1ac?sid=d9923bcd-5030-42d9-9b7f-170df5297cfd ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules --- .../captain/pageComponents/DeleteDialog.vue | 3 +++ .../dashboard/routes/dashboard/captain/documents/Index.vue | 7 +++++++ .../dashboard/routes/dashboard/captain/responses/Index.vue | 7 +++++++ 3 files changed, 17 insertions(+) diff --git a/app/javascript/dashboard/components-next/captain/pageComponents/DeleteDialog.vue b/app/javascript/dashboard/components-next/captain/pageComponents/DeleteDialog.vue index 3301e085d..31e18394f 100644 --- a/app/javascript/dashboard/components-next/captain/pageComponents/DeleteDialog.vue +++ b/app/javascript/dashboard/components-next/captain/pageComponents/DeleteDialog.vue @@ -20,6 +20,8 @@ const props = defineProps({ }, }); +const emit = defineEmits(['deleteSuccess']); + const { t } = useI18n(); const store = useStore(); const deleteDialogRef = ref(null); @@ -30,6 +32,7 @@ const deleteEntity = async payload => { try { await store.dispatch(`captain${props.type}/delete`, payload); + emit('deleteSuccess'); useAlert(t(`CAPTAIN.${i18nKey.value}.DELETE.SUCCESS_MESSAGE`)); } catch (error) { useAlert(t(`CAPTAIN.${i18nKey.value}.DELETE.ERROR_MESSAGE`)); diff --git a/app/javascript/dashboard/routes/dashboard/captain/documents/Index.vue b/app/javascript/dashboard/routes/dashboard/captain/documents/Index.vue index af2cbc0ef..cfe088055 100644 --- a/app/javascript/dashboard/routes/dashboard/captain/documents/Index.vue +++ b/app/javascript/dashboard/routes/dashboard/captain/documents/Index.vue @@ -85,6 +85,12 @@ const handleAssistantFilterChange = assistant => { const onPageChange = page => fetchDocuments(page); +const onDeleteSuccess = () => { + if (documents.value?.length === 0 && documentsMeta.value?.page > 1) { + onPageChange(documentsMeta.value.page - 1); + } +}; + onMounted(() => { if (!assistants.value.length) { store.dispatch('captainAssistants/get'); @@ -146,6 +152,7 @@ onMounted(() => { ref="deleteDocumentDialog" :entity="selectedDocument" type="Documents" + @delete-success="onDeleteSuccess" /> diff --git a/app/javascript/dashboard/routes/dashboard/captain/responses/Index.vue b/app/javascript/dashboard/routes/dashboard/captain/responses/Index.vue index 6fddcd465..e62f8f0e1 100644 --- a/app/javascript/dashboard/routes/dashboard/captain/responses/Index.vue +++ b/app/javascript/dashboard/routes/dashboard/captain/responses/Index.vue @@ -129,6 +129,12 @@ const fetchResponses = (page = 1) => { const onPageChange = page => fetchResponses(page); +const onDeleteSuccess = () => { + if (responses.value?.length === 0 && responseMeta.value?.page > 1) { + onPageChange(responseMeta.value.page - 1); + } +}; + const handleStatusFilterChange = ({ value }) => { selectedStatus.value = value; isStatusFilterOpen.value = false; @@ -211,6 +217,7 @@ onMounted(() => { ref="deleteDialog" :entity="selectedResponse" type="Responses" + @delete-success="onDeleteSuccess" /> Date: Mon, 20 Jan 2025 14:34:33 +0530 Subject: [PATCH 005/105] fix: Vite dev build fails due to sass (#10716) # Pull Request Template ## Description Fixes https://linear.app/chatwoot/issue/CW-3853/vite-dev-build-fails-due-to-dart-sass **Other issue** | First Header | Second Header | | ------------- | ------------- | | ![image](https://github.com/user-attachments/assets/e99ac2bc-0b8a-44d1-aa58-9e10e00b5ad5) | ![image](https://github.com/user-attachments/assets/c987f70c-f5f5-4606-a498-00a6fe6b6525) | ## Type of change - [x] Bug fix (non-breaking change which fixes an issue) ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules --- .../reports/components/Filters/Labels.vue | 15 ++++------ .../reports/components/ReportFilters.vue | 28 ++++++++----------- .../reports/components/ReportsWrapper.vue | 8 +++--- 3 files changed, 22 insertions(+), 29 deletions(-) diff --git a/app/javascript/dashboard/routes/dashboard/settings/reports/components/Filters/Labels.vue b/app/javascript/dashboard/routes/dashboard/settings/reports/components/Filters/Labels.vue index 655e82f7b..68cc4baf2 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/reports/components/Filters/Labels.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/reports/components/Filters/Labels.vue @@ -39,15 +39,13 @@ export default { @update:model-value="handleInput" > @@ -57,10 +55,9 @@ export default { :style="{ backgroundColor: props.option.color }" class="flex-shrink-0 w-5 h-5 border border-solid rounded-full border-slate-100 dark:border-slate-800" /> - - - {{ props.option.title }} - + + + {{ props.option.title }} diff --git a/app/javascript/dashboard/routes/dashboard/settings/reports/components/ReportFilters.vue b/app/javascript/dashboard/routes/dashboard/settings/reports/components/ReportFilters.vue index b41851180..053ae7cd7 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/reports/components/ReportFilters.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/reports/components/ReportFilters.vue @@ -179,18 +179,16 @@ export default { @update:model-value="changeFilterSelection" >