Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 21 additions & 6 deletions app/javascript/template_builder/builder.vue
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@
@close="isShowSigningOrderModal = false"
/>
</Teleport>
<Toast ref="toast" />
</div>
</template>

Expand All @@ -364,6 +365,7 @@ import DocumentControls from './controls'
import MobileFields from './mobile_fields'
import FieldSubmitter from './field_submitter'
import SigningOrderModal from './signing_order_modal'
import Toast from './toast'
import { IconPlus, IconUsersPlus, IconDeviceFloppy, IconChevronDown, IconEye, IconWritingSign, IconInnerShadowTop, IconInfoCircle, IconAdjustments } from '@tabler/icons-vue'
import { v4 } from 'uuid'
import { ref, computed, toRaw, watch } from 'vue'
Expand Down Expand Up @@ -393,7 +395,8 @@ export default {
IconAdjustments,
IconEye,
IconDeviceFloppy,
SigningOrderModal
SigningOrderModal,
Toast
},
provide () {
return {
Expand Down Expand Up @@ -1524,7 +1527,7 @@ export default {
method: 'POST',
body: formData
}).then(async (resp) => {
this.updateFromUpload(await resp.json())
this.updateFromUpload(await resp.json(), { showToast: false })
}).finally(() => {
this.isLoadingBlankPage = false
})
Expand All @@ -1533,7 +1536,11 @@ export default {
onUploadFailed (error) {
if (error) alert(error)
},
updateFromUpload (data) {
updateFromUpload (data, { showToast = true } = {}) {
if (showToast) {
this.$refs.toast.show(this.t('document_uploaded_successfully'))
}

this.template.schema.push(...data.schema)
this.template.documents.push(...data.documents)

Expand Down Expand Up @@ -1606,7 +1613,7 @@ export default {
this.save()
}
},
onDocumentReplace (data) {
onDocumentReplace (data, { showToast = true } = {}) {
const { replaceSchemaItem, schema, documents } = data

this.template.schema.splice(this.template.schema.indexOf(replaceSchemaItem), 1, { ...replaceSchemaItem, ...schema[0] })
Expand Down Expand Up @@ -1651,6 +1658,10 @@ export default {
this.onUpload(this.template)
}

if (showToast) {
this.$refs.toast.show(this.t('document_uploaded_successfully'))
}

this.save()
},
onDocumentsReplace (data) {
Expand All @@ -1662,16 +1673,20 @@ export default {
replaceSchemaItem: existingSchemaItem,
schema: [schemaItem],
documents: [data.documents.find((doc) => doc.uuid === schemaItem.attachment_uuid)]
})
}, { showToast: false })
} else {
this.updateFromUpload({
schema: [schemaItem],
documents: [data.documents.find((doc) => doc.uuid === schemaItem.attachment_uuid)],
fields: data.fields,
submitters: data.submitters
})
}, { showToast: false })
}
})

if (data.schema.length) {
this.$refs.toast.show(this.t('document_uploaded_successfully'))
}
},
onDocumentsReplaceAndTemplateClone (template) {
window.Turbo.visit(`/templates/${template.id}/edit`)
Expand Down
6 changes: 6 additions & 0 deletions app/javascript/template_builder/i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ const en = {
replace: 'Replace',
uploading_: 'Uploading...',
add_document: 'Add Document',
document_uploaded_successfully: 'Document uploaded successfully',
none: 'None',
ssn: 'SSN',
ein: 'EIN',
Expand Down Expand Up @@ -276,6 +277,7 @@ const es = {
replace: 'Reemplazar',
uploading_: 'Subiendo...',
add_document: 'Subir',
document_uploaded_successfully: 'Documento cargado correctamente',
any: 'Cualquier',
drawn: 'Dibujado',
drawn_or_typed: 'Dibujado o Escrito',
Expand Down Expand Up @@ -432,6 +434,7 @@ const it = {
replace: 'Sostituisci',
uploading_: 'Caricamento in corso...',
add_document: 'Aggiungi',
document_uploaded_successfully: 'Documento caricato correttamente',
none: 'Nessuno',
ssn: 'SSN',
ein: 'EIN',
Expand Down Expand Up @@ -578,6 +581,7 @@ const pt = {
replace: 'Substituir',
uploading_: 'Carregando...',
add_document: 'Enviar',
document_uploaded_successfully: 'Documento enviado com sucesso',
any: 'Qualquer',
drawn: 'Desenhado',
drawn_or_typed: 'Desenhado ou Digitado',
Expand Down Expand Up @@ -729,6 +733,7 @@ const fr = {
replace: 'Remplacer',
uploading_: 'Téléchargement en cours...',
add_document: 'Télécharger',
document_uploaded_successfully: 'Document téléchargé avec succès',
any: 'Tout',
drawn: 'Dessiné',
drawn_or_typed: 'Dessiné ou Tapé',
Expand Down Expand Up @@ -880,6 +885,7 @@ const de = {
replace: 'Ersetzen',
uploading_: 'Hochladen...',
add_document: 'Hochladen',
document_uploaded_successfully: 'Dokument erfolgreich hochgeladen',
any: 'Jede',
drawn: 'Gezeichnet',
drawn_or_typed: 'Gezeichnet oder getippt',
Expand Down
48 changes: 48 additions & 0 deletions app/javascript/template_builder/toast.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<template>
<div
v-if="isVisible"
id="upload_success_toast"
class="toast toast-bottom toast-end z-50"
role="status"
aria-live="polite"
aria-atomic="true"
>
<div class="alert alert-success">
<IconCircleCheck class="w-6 h-6 flex-none" />
<span>{{ message }}</span>
</div>
</div>
</template>

<script>
import { IconCircleCheck } from '@tabler/icons-vue'

export default {
name: 'BuilderToast',
components: {
IconCircleCheck
},
data () {
return {
isVisible: false,
message: '',
dismissTimeout: null
}
},
beforeUnmount () {
clearTimeout(this.dismissTimeout)
},
methods: {
show (message, duration = 4000) {
this.message = message
this.isVisible = true

clearTimeout(this.dismissTimeout)

this.dismissTimeout = setTimeout(() => {
this.isVisible = false
}, duration)
}
}
}
</script>
25 changes: 25 additions & 0 deletions spec/system/template_builder_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,31 @@
end.to change { template.documents.count }.by(1)

expect(page).to have_content('sample-image')
expect(page).to have_css('#upload_success_toast', text: 'Document uploaded successfully')
expect(page).to have_css('#upload_success_toast[role="status"][aria-live="polite"]')
end
end

context 'when uploading to an empty template' do
let(:template) { create(:template, account:, author:, attachment_count: 0) }

before do
visit edit_template_path(template)
end

it 'shows a success toast after the first document upload' do
expect(page).to have_css('#document_dropzone')

expect do
find('#document_dropzone input[type="file"]', visible: false)
.attach_file(Rails.root.join('spec/fixtures/sample-image.png'))

page.driver.wait_for_network_idle
end.to change { template.reload.documents.count }.by(1)

expect(page).to have_content('sample-image')
expect(page).to have_css('#upload_success_toast', text: 'Document uploaded successfully')
expect(page).to have_css('#upload_success_toast[role="status"][aria-live="polite"]')
end
end

Expand Down
Loading