From 8627400e6a944fb60134e986b930dc8b0ddea1ff Mon Sep 17 00:00:00 2001 From: Tanmay Deep Sharma Date: Wed, 6 Aug 2025 12:07:52 +0530 Subject: [PATCH 1/4] feat: add fe changes for captain pdf support for faq generation --- .../dashboard/api/helpCenter/pdfDocuments.js | 29 ++ .../PdfDocumentsPage/ContentGeneration.vue | 284 ++++++++++++++++++ .../PdfDocumentsPage/PdfDocumentsPage.vue | 40 +++ .../captain/assistant/DocumentCard.vue | 16 +- .../document/CreateDocumentDialog.vue | 4 +- .../pageComponents/document/DocumentForm.vue | 140 ++++++++- .../dashboard/i18n/locale/en/helpCenter.json | 52 ++++ .../i18n/locale/en/integrations.json | 17 ++ .../dashboard/helpcenter/helpcenter.routes.js | 9 + package.json | 17 +- pnpm-lock.yaml | 38 ++- 11 files changed, 615 insertions(+), 31 deletions(-) create mode 100644 app/javascript/dashboard/api/helpCenter/pdfDocuments.js create mode 100644 app/javascript/dashboard/components-next/HelpCenter/Pages/PdfDocumentsPage/ContentGeneration.vue create mode 100644 app/javascript/dashboard/components-next/HelpCenter/Pages/PdfDocumentsPage/PdfDocumentsPage.vue diff --git a/app/javascript/dashboard/api/helpCenter/pdfDocuments.js b/app/javascript/dashboard/api/helpCenter/pdfDocuments.js new file mode 100644 index 000000000..876678edf --- /dev/null +++ b/app/javascript/dashboard/api/helpCenter/pdfDocuments.js @@ -0,0 +1,29 @@ +/* global axios */ +import ApiClient from '../ApiClient'; + +class PdfDocumentsAPI extends ApiClient { + constructor() { + super('portals', { accountScoped: true }); + } + + uploadContent(portalSlug, formData) { + return axios.post(`${this.url}/${portalSlug}/upload_content`, formData, { + headers: { + 'Content-Type': 'multipart/form-data', + }, + }); + } + + getGeneratedContent(portalSlug) { + return axios.get(`${this.url}/${portalSlug}/generated_content`); + } + + publishContent(portalSlug, responseIds, categoryId = null) { + return axios.post(`${this.url}/${portalSlug}/publish_content`, { + response_ids: responseIds, + category_id: categoryId + }); + } +} + +export default PdfDocumentsAPI; \ No newline at end of file diff --git a/app/javascript/dashboard/components-next/HelpCenter/Pages/PdfDocumentsPage/ContentGeneration.vue b/app/javascript/dashboard/components-next/HelpCenter/Pages/PdfDocumentsPage/ContentGeneration.vue new file mode 100644 index 000000000..028bf090b --- /dev/null +++ b/app/javascript/dashboard/components-next/HelpCenter/Pages/PdfDocumentsPage/ContentGeneration.vue @@ -0,0 +1,284 @@ + + + + + \ No newline at end of file diff --git a/app/javascript/dashboard/components-next/HelpCenter/Pages/PdfDocumentsPage/PdfDocumentsPage.vue b/app/javascript/dashboard/components-next/HelpCenter/Pages/PdfDocumentsPage/PdfDocumentsPage.vue new file mode 100644 index 000000000..a10219513 --- /dev/null +++ b/app/javascript/dashboard/components-next/HelpCenter/Pages/PdfDocumentsPage/PdfDocumentsPage.vue @@ -0,0 +1,40 @@ + + + \ No newline at end of file diff --git a/app/javascript/dashboard/components-next/captain/assistant/DocumentCard.vue b/app/javascript/dashboard/components-next/captain/assistant/DocumentCard.vue index d6e546581..94fdf6c64 100644 --- a/app/javascript/dashboard/components-next/captain/assistant/DocumentCard.vue +++ b/app/javascript/dashboard/components-next/captain/assistant/DocumentCard.vue @@ -63,6 +63,18 @@ const menuItems = computed(() => { const createdAt = computed(() => dynamicTime(props.createdAt)); +const isPdfDocument = computed(() => props.externalLink?.startsWith('PDF:')); +const displayLink = computed(() => { + if (isPdfDocument.value) { + // Remove 'PDF: ' prefix and timestamp suffix for display + const fullName = props.externalLink.substring(5); + // Remove timestamp suffix (_YYYYMMDDHHMMSS) if present + return fullName.replace(/_\d{14}$/, ''); + } + return props.externalLink; +}); +const linkIcon = computed(() => isPdfDocument.value ? 'i-ph-file-pdf' : 'i-ph-link-simple'); + const handleAction = ({ action, value }) => { toggleDropdown(false); emit('action', { action, value, id: props.id }); @@ -106,8 +118,8 @@ const handleAction = ({ action, value }) => { - - {{ externalLink }} + + {{ displayLink }}
{{ createdAt }} diff --git a/app/javascript/dashboard/components-next/captain/pageComponents/document/CreateDocumentDialog.vue b/app/javascript/dashboard/components-next/captain/pageComponents/document/CreateDocumentDialog.vue index 7db74f523..fc7591966 100644 --- a/app/javascript/dashboard/components-next/captain/pageComponents/document/CreateDocumentDialog.vue +++ b/app/javascript/dashboard/components-next/captain/pageComponents/document/CreateDocumentDialog.vue @@ -23,7 +23,9 @@ const handleSubmit = async newDocument => { dialogRef.value.close(); } catch (error) { const errorMessage = - error?.response?.message || t(`${i18nKey}.ERROR_MESSAGE`); + error?.response?.data?.message || + error?.response?.message || + t(`${i18nKey}.ERROR_MESSAGE`); useAlert(errorMessage); } }; diff --git a/app/javascript/dashboard/components-next/captain/pageComponents/document/DocumentForm.vue b/app/javascript/dashboard/components-next/captain/pageComponents/document/DocumentForm.vue index d85ba8a06..a191fc184 100644 --- a/app/javascript/dashboard/components-next/captain/pageComponents/document/DocumentForm.vue +++ b/app/javascript/dashboard/components-next/captain/pageComponents/document/DocumentForm.vue @@ -1,9 +1,10 @@ - - \ No newline at end of file diff --git a/app/javascript/dashboard/components-next/HelpCenter/Pages/PdfDocumentsPage/PdfDocumentsPage.vue b/app/javascript/dashboard/components-next/HelpCenter/Pages/PdfDocumentsPage/PdfDocumentsPage.vue deleted file mode 100644 index a10219513..000000000 --- a/app/javascript/dashboard/components-next/HelpCenter/Pages/PdfDocumentsPage/PdfDocumentsPage.vue +++ /dev/null @@ -1,40 +0,0 @@ - - - \ No newline at end of file diff --git a/app/javascript/dashboard/components-next/captain/assistant/DocumentCard.vue b/app/javascript/dashboard/components-next/captain/assistant/DocumentCard.vue index 94fdf6c64..a81324008 100644 --- a/app/javascript/dashboard/components-next/captain/assistant/DocumentCard.vue +++ b/app/javascript/dashboard/components-next/captain/assistant/DocumentCard.vue @@ -73,7 +73,9 @@ const displayLink = computed(() => { } return props.externalLink; }); -const linkIcon = computed(() => isPdfDocument.value ? 'i-ph-file-pdf' : 'i-ph-link-simple'); +const linkIcon = computed(() => + isPdfDocument.value ? 'i-ph-file-pdf' : 'i-ph-link-simple' +); const handleAction = ({ action, value }) => { toggleDropdown(false); diff --git a/app/javascript/dashboard/components-next/captain/pageComponents/document/CreateDocumentDialog.vue b/app/javascript/dashboard/components-next/captain/pageComponents/document/CreateDocumentDialog.vue index fc7591966..1762f8df4 100644 --- a/app/javascript/dashboard/components-next/captain/pageComponents/document/CreateDocumentDialog.vue +++ b/app/javascript/dashboard/components-next/captain/pageComponents/document/CreateDocumentDialog.vue @@ -23,8 +23,8 @@ const handleSubmit = async newDocument => { dialogRef.value.close(); } catch (error) { const errorMessage = - error?.response?.data?.message || - error?.response?.message || + error?.response?.data?.message || + error?.response?.message || t(`${i18nKey}.ERROR_MESSAGE`); useAlert(errorMessage); } diff --git a/app/javascript/dashboard/components-next/captain/pageComponents/document/DocumentForm.vue b/app/javascript/dashboard/components-next/captain/pageComponents/document/DocumentForm.vue index a191fc184..32efbaeac 100644 --- a/app/javascript/dashboard/components-next/captain/pageComponents/document/DocumentForm.vue +++ b/app/javascript/dashboard/components-next/captain/pageComponents/document/DocumentForm.vue @@ -2,7 +2,7 @@ import { reactive, computed, ref, nextTick } from 'vue'; import { useI18n } from 'vue-i18n'; import { useVuelidate } from '@vuelidate/core'; -import { required, minLength, url, requiredIf } from '@vuelidate/validators'; +import { required, minLength, requiredIf } from '@vuelidate/validators'; import { useMapGetter } from 'dashboard/composables/store'; import { useAlert } from 'dashboard/composables'; @@ -31,14 +31,14 @@ const state = reactive({ ...initialState }); const fileInputRef = ref(null); const validationRules = { - url: { - required: requiredIf(() => state.documentType === 'url'), - url: requiredIf(() => state.documentType === 'url'), - minLength: minLength(1) + url: { + required: requiredIf(() => state.documentType === 'url'), + url: requiredIf(() => state.documentType === 'url'), + minLength: minLength(1), }, assistantId: { required }, - pdfFile: { - required: requiredIf(() => state.documentType === 'pdf') + pdfFile: { + required: requiredIf(() => state.documentType === 'pdf'), }, }; @@ -67,7 +67,7 @@ const formErrors = computed(() => ({ const handleCancel = () => emit('cancel'); -const handleFileChange = (event) => { +const handleFileChange = event => { const file = event.target.files[0]; if (file) { if (file.type !== 'application/pdf') { @@ -75,7 +75,8 @@ const handleFileChange = (event) => { event.target.value = ''; return; } - if (file.size > 512 * 1024 * 1024) { // 512MB + if (file.size > 20 * 1024 * 1024) { + // 20MB useAlert(t('CAPTAIN.DOCUMENTS.FORM.PDF_FILE.TOO_LARGE')); event.target.value = ''; return; @@ -97,16 +98,19 @@ const openFileDialog = () => { const prepareDocumentDetails = () => { const formData = new FormData(); formData.append('document[assistant_id]', state.assistantId); - + if (state.documentType === 'url') { formData.append('document[external_link]', state.url); formData.append('document[name]', state.name || state.url); } else { formData.append('document[pdf_file]', state.pdfFile); - formData.append('document[name]', state.name || state.pdfFile.name); + formData.append( + 'document[name]', + state.name || state.pdfFile.name.replace('.pdf', '') + ); // No need to send external_link for PDF - it's auto-generated in the backend } - + return formData; }; @@ -129,22 +133,26 @@ const handleSubmit = async () => {