google drive file picker
This commit is contained in:
@@ -49,6 +49,8 @@ import ShowOnValue from './elements/show_on_value'
|
||||
import CustomValidation from './elements/custom_validation'
|
||||
import ToggleClasses from './elements/toggle_classes'
|
||||
import AutosizeField from './elements/autosize_field'
|
||||
import GoogleDriveFilePicker from './elements/google_drive_file_picker'
|
||||
import OpenModal from './elements/open_modal'
|
||||
|
||||
import * as TurboInstantClick from './lib/turbo_instant_click'
|
||||
|
||||
@@ -136,6 +138,8 @@ safeRegisterElement('show-on-value', ShowOnValue)
|
||||
safeRegisterElement('custom-validation', CustomValidation)
|
||||
safeRegisterElement('toggle-classes', ToggleClasses)
|
||||
safeRegisterElement('autosize-field', AutosizeField)
|
||||
safeRegisterElement('google-drive-file-picker', GoogleDriveFilePicker)
|
||||
safeRegisterElement('open-modal', OpenModal)
|
||||
|
||||
safeRegisterElement('template-builder', class extends HTMLElement {
|
||||
connectedCallback () {
|
||||
@@ -160,6 +164,7 @@ safeRegisterElement('template-builder', class extends HTMLElement {
|
||||
withSendButton: this.dataset.withSendButton !== 'false',
|
||||
withSignYourselfButton: this.dataset.withSignYourselfButton !== 'false',
|
||||
withConditions: this.dataset.withConditions === 'true',
|
||||
withGoogleDrive: this.dataset.withGoogleDrive === 'true',
|
||||
withReplaceAndCloneUpload: true,
|
||||
currencies: (this.dataset.currencies || '').split(',').filter(Boolean),
|
||||
acceptFileTypes: this.dataset.acceptFileTypes,
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
export default class extends HTMLElement {
|
||||
connectedCallback () {
|
||||
const iframeTemplate = this.querySelector('template')
|
||||
|
||||
this.observer = new IntersectionObserver((entries) => {
|
||||
if (entries.some(e => e.isIntersecting)) {
|
||||
iframeTemplate.parentElement.prepend(iframeTemplate.content)
|
||||
|
||||
this.observer.disconnect()
|
||||
}
|
||||
})
|
||||
|
||||
this.observer.observe(this)
|
||||
|
||||
window.addEventListener('message', this.messageHandler)
|
||||
}
|
||||
|
||||
messageHandler = (event) => {
|
||||
if (event.data.type === 'google-drive-files-picked') {
|
||||
this.form.querySelectorAll('input[name="google_drive_file_ids[]"]').forEach(el => el.remove())
|
||||
|
||||
const files = event.data.files || []
|
||||
|
||||
files.forEach((file) => {
|
||||
const input = document.createElement('input')
|
||||
input.type = 'hidden'
|
||||
input.name = 'google_drive_file_ids[]'
|
||||
input.value = file.id
|
||||
this.form.appendChild(input)
|
||||
})
|
||||
|
||||
this.form.querySelector('button[type="submit"]').click()
|
||||
this.loader.classList.remove('hidden')
|
||||
} else if (event.data.type === 'google-drive-picker-loaded') {
|
||||
this.loader.classList.add('hidden')
|
||||
this.form.classList.remove('hidden')
|
||||
} else if (event.data.type === 'google-drive-picker-request-oauth') {
|
||||
document.getElementById(this.dataset.oauthButtonId).classList.remove('hidden')
|
||||
this.classList.add('hidden')
|
||||
}
|
||||
}
|
||||
|
||||
disconnectedCallback () {
|
||||
this.observer?.unobserve(this)
|
||||
window.removeEventListener('message', this.messageHandler)
|
||||
}
|
||||
|
||||
get form () {
|
||||
return this.querySelector('form')
|
||||
}
|
||||
|
||||
get loader () {
|
||||
return document.getElementById('google_drive_loader')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
export default class extends HTMLElement {
|
||||
connectedCallback () {
|
||||
const src = this.getAttribute('src')
|
||||
const link = document.createElement('a')
|
||||
|
||||
link.href = src
|
||||
link.setAttribute('data-turbo-frame', 'modal')
|
||||
link.style.display = 'none'
|
||||
|
||||
this.appendChild(link)
|
||||
|
||||
link.click()
|
||||
|
||||
window.history.replaceState({}, document.title, window.location.pathname)
|
||||
}
|
||||
}
|
||||
@@ -260,8 +260,12 @@
|
||||
:style="{ backgroundColor }"
|
||||
>
|
||||
<Upload
|
||||
v-if="sortedDocuments.length && editable && withUploadButton"
|
||||
v-if="editable && withUploadButton"
|
||||
v-show="sortedDocuments.length"
|
||||
ref="upload"
|
||||
:accept-file-types="acceptFileTypes"
|
||||
:authenticity-token="authenticityToken"
|
||||
:with-google-drive="withGoogleDrive"
|
||||
:template-id="template.id"
|
||||
@success="updateFromUpload"
|
||||
/>
|
||||
@@ -297,6 +301,8 @@
|
||||
v-if="withUploadButton"
|
||||
:template-id="template.id"
|
||||
:accept-file-types="acceptFileTypes"
|
||||
:with-google-drive="withGoogleDrive"
|
||||
@click-google-drive="$refs.upload.openGoogleDriveModal()"
|
||||
@success="updateFromUpload"
|
||||
/>
|
||||
<button
|
||||
@@ -368,6 +374,8 @@
|
||||
v-if="withUploadButton"
|
||||
:template-id="template.id"
|
||||
:accept-file-types="acceptFileTypes"
|
||||
:authenticity-token="authenticityToken"
|
||||
:with-google-drive="withGoogleDrive"
|
||||
@success="updateFromUpload"
|
||||
/>
|
||||
<button
|
||||
@@ -766,6 +774,11 @@ export default {
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
withGoogleDrive: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
onlyDefinedFields: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
@@ -1757,8 +1770,9 @@ export default {
|
||||
},
|
||||
onDocumentReplace (data) {
|
||||
const { replaceSchemaItem, schema, documents } = data
|
||||
const { google_drive_file_id, ...cleanedReplaceSchemaItem } = replaceSchemaItem
|
||||
|
||||
this.template.schema.splice(this.template.schema.indexOf(replaceSchemaItem), 1, { ...replaceSchemaItem, ...schema[0] })
|
||||
this.template.schema.splice(this.template.schema.indexOf(replaceSchemaItem), 1, { ...cleanedReplaceSchemaItem, ...schema[0] })
|
||||
this.template.documents.push(...documents)
|
||||
|
||||
if (data.fields) {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
:contenteditable="editable"
|
||||
style="min-width: 2px"
|
||||
:class="iconInline ? 'inline' : 'block'"
|
||||
class="peer outline-none focus:block"
|
||||
class="peer outline-none"
|
||||
@paste.prevent="onPaste"
|
||||
@keydown.enter.prevent="blurContenteditable"
|
||||
@focus="$emit('focus', $event)"
|
||||
@@ -26,10 +26,10 @@
|
||||
*
|
||||
</span>
|
||||
<IconPencil
|
||||
class="cursor-pointer flex-none opacity-0 group-hover/contenteditable-container:opacity-100 group-hover/contenteditable:opacity-100 align-middle peer-focus:hidden"
|
||||
class="cursor-pointer flex-none opacity-0 group-hover/contenteditable-container:opacity-100 group-hover/contenteditable:opacity-100 align-middle"
|
||||
:style="iconInline ? {} : { right: -(1.1 * iconWidth) + 'px' }"
|
||||
:title="t('edit')"
|
||||
:class="{ invisible: !editable, 'ml-1': !withRequired, 'absolute': !iconInline, 'inline align-bottom': iconInline }"
|
||||
:class="{ invisible: !editable, 'ml-1': !withRequired, 'absolute': !iconInline, 'inline align-bottom': iconInline, 'peer-focus:hidden': hideIcon, 'peer-focus:invisible': !hideIcon }"
|
||||
:width="iconWidth"
|
||||
:stroke-width="iconStrokeWidth"
|
||||
@click="[focusContenteditable(), selectOnEditClick && selectContent()]"
|
||||
@@ -62,6 +62,11 @@ export default {
|
||||
required: false,
|
||||
default: 30
|
||||
},
|
||||
hideIcon: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true
|
||||
},
|
||||
withRequired: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
:for="inputId"
|
||||
:class="{ 'opacity-50': isLoading, 'hover:bg-base-200/50': withHoverClass && !isDragEntering, 'bg-base-200/50 border-base-content/30': isDragEntering }"
|
||||
>
|
||||
<div class="absolute top-0 right-0 left-0 bottom-0 flex items-center justify-center pointer-events-none">
|
||||
<div class="flex flex-col items-center">
|
||||
<div class="absolute top-0 right-0 left-0 bottom-0 flex items-center justify-center">
|
||||
<div class="flex flex-col items-center pointer-events-none">
|
||||
<IconInnerShadowTop
|
||||
v-if="isLoading"
|
||||
class="animate-spin"
|
||||
@@ -40,6 +40,15 @@
|
||||
>
|
||||
<span class="font-medium">{{ t('click_to_upload') }}</span> {{ t('or_drag_and_drop_files') }}
|
||||
</div>
|
||||
<button
|
||||
v-if="withGoogleDrive"
|
||||
class="flex items-center text-sm mt-2 pointer-events-auto"
|
||||
@click.stop.prevent="$emit('click-google-drive')"
|
||||
>
|
||||
<span>{{ t('or_add_from') }}</span>
|
||||
<IconBrandGoogleDrive class="w-4 h-4 inline-block ml-1" />
|
||||
<span class="ml-1 font-medium hover:underline">Google Drive</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<form
|
||||
@@ -62,7 +71,7 @@
|
||||
|
||||
<script>
|
||||
import Upload from './upload'
|
||||
import { IconCloudUpload, IconFilePlus, IconFileSymlink, IconFiles, IconInnerShadowTop } from '@tabler/icons-vue'
|
||||
import { IconCloudUpload, IconFilePlus, IconFileSymlink, IconFiles, IconInnerShadowTop, IconBrandGoogleDrive } from '@tabler/icons-vue'
|
||||
|
||||
export default {
|
||||
name: 'FileDropzone',
|
||||
@@ -71,7 +80,8 @@ export default {
|
||||
IconCloudUpload,
|
||||
IconInnerShadowTop,
|
||||
IconFileSymlink,
|
||||
IconFiles
|
||||
IconFiles,
|
||||
IconBrandGoogleDrive
|
||||
},
|
||||
inject: ['baseFetch', 't'],
|
||||
props: {
|
||||
@@ -99,6 +109,11 @@ export default {
|
||||
required: false,
|
||||
default: true
|
||||
},
|
||||
withGoogleDrive: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: false,
|
||||
@@ -110,7 +125,7 @@ export default {
|
||||
default: 'image/*, application/pdf, application/zip'
|
||||
}
|
||||
},
|
||||
emits: ['success', 'error', 'loading'],
|
||||
emits: ['success', 'error', 'loading', 'click-google-drive'],
|
||||
data () {
|
||||
return {
|
||||
isLoading: false,
|
||||
|
||||
@@ -125,7 +125,7 @@
|
||||
@update:model-value="$emit('name-change', selectedSubmitter)"
|
||||
/>
|
||||
</div>
|
||||
<span class="flex items-center transition-all duration-75 group-hover:border border-base-content/20 border-dashed w-6 h-6 flex justify-center items-center rounded">
|
||||
<span class="flex items-center transition-all duration-75 group-hover:border border-base-content/20 border-dashed w-6 h-6 justify-center rounded">
|
||||
<component
|
||||
:is="editable ? 'IconPlus' : 'IconChevronDown'"
|
||||
width="18"
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
<template>
|
||||
<div
|
||||
class="dropdown"
|
||||
:class="{ 'dropdown-open': isLoading }"
|
||||
>
|
||||
<label tabindex="0">
|
||||
<IconBrandGoogleDrive class="w-5 h-5 inline-block mr-1 cursor-pointer" />
|
||||
</label>
|
||||
<ul
|
||||
tabindex="0"
|
||||
:style="{ backgroundColor }"
|
||||
class="dropdown-content z-[1] shadow menu rounded-box"
|
||||
>
|
||||
<li>
|
||||
<a
|
||||
:href="`https://drive.google.com/file/d/${googleDriveFileId}/view?usp=sharing`"
|
||||
data-turbo="false"
|
||||
target="_blank"
|
||||
class="flex items-center"
|
||||
>
|
||||
<IconExternalLink class="w-4 h-4 flex-shrink-0" />
|
||||
<span>{{ t('view') }}</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<form
|
||||
ref="form"
|
||||
class="flex items-center"
|
||||
@submit.prevent="upload({ path: uploadUrl })"
|
||||
>
|
||||
<input
|
||||
:id="inputId"
|
||||
ref="input"
|
||||
:value="googleDriveFileId"
|
||||
type="hidden"
|
||||
name="google_drive_file_ids[]"
|
||||
>
|
||||
<button
|
||||
type="submit"
|
||||
:disabled="isLoading"
|
||||
class="flex items-center w-full space-x-2"
|
||||
>
|
||||
<IconRefresh
|
||||
class="w-4 h-4 flex-shrink-0"
|
||||
:class="{ 'animate-spin': isLoading }"
|
||||
/>
|
||||
<span>{{ message }}</span>
|
||||
</button>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Upload from './upload'
|
||||
import { IconRefresh, IconBrandGoogleDrive, IconExternalLink } from '@tabler/icons-vue'
|
||||
|
||||
export default {
|
||||
name: 'GoogleDriveDocumentSettings',
|
||||
components: {
|
||||
IconRefresh,
|
||||
IconBrandGoogleDrive,
|
||||
IconExternalLink
|
||||
},
|
||||
inject: ['baseFetch', 't', 'backgroundColor'],
|
||||
props: {
|
||||
templateId: {
|
||||
type: [Number, String],
|
||||
required: true
|
||||
},
|
||||
googleDriveFileId: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
emits: ['success'],
|
||||
data () {
|
||||
return {
|
||||
isLoading: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
inputId () {
|
||||
return 'el' + Math.random().toString(32).split('.')[1]
|
||||
},
|
||||
uploadUrl () {
|
||||
return `/templates/${this.templateId}/google_drive_documents`
|
||||
},
|
||||
message () {
|
||||
if (this.isLoading) {
|
||||
return this.t('syncing')
|
||||
} else {
|
||||
return this.t('sync')
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
upload: Upload.methods.upload
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,4 +1,5 @@
|
||||
const en = {
|
||||
view: 'View',
|
||||
payment_link: 'Payment link',
|
||||
strikeout: 'Strikeout',
|
||||
draw_strikethrough_the_document: 'Draw strikethrough the document',
|
||||
@@ -176,10 +177,14 @@ const en = {
|
||||
and: 'and',
|
||||
or: 'or',
|
||||
start_a_quick_tour_to_learn_how_to_create_an_send_your_first_document: 'Start a quick tour to learn how to create an send your first document',
|
||||
start_tour: 'Start Tour'
|
||||
start_tour: 'Start Tour',
|
||||
or_add_from: 'Or add from',
|
||||
sync: 'Sync',
|
||||
syncing: 'Syncing...'
|
||||
}
|
||||
|
||||
const es = {
|
||||
view: 'Vista',
|
||||
payment_link: 'Enlace de pago',
|
||||
strikeout: 'Tachar',
|
||||
draw_strikethrough_the_document: 'Dibujar una línea de tachado en el documento',
|
||||
@@ -357,10 +362,14 @@ const es = {
|
||||
and: 'y',
|
||||
or: 'o',
|
||||
start_a_quick_tour_to_learn_how_to_create_an_send_your_first_document: 'Inicia una guía rápida para aprender a crear y enviar tu primer documento.',
|
||||
start_tour: 'Iniciar guía'
|
||||
start_tour: 'Iniciar guía',
|
||||
or_add_from: 'O agregar desde',
|
||||
sync: 'Sincronizar',
|
||||
syncing: 'Sincronizando...'
|
||||
}
|
||||
|
||||
const it = {
|
||||
view: 'Vista',
|
||||
payment_link: 'Link di pagamento',
|
||||
strikeout: 'Barrato',
|
||||
draw_strikethrough_the_document: 'Disegna una linea barrata sul documento',
|
||||
@@ -538,10 +547,14 @@ const it = {
|
||||
and: 'e',
|
||||
or: 'o',
|
||||
start_a_quick_tour_to_learn_how_to_create_an_send_your_first_document: 'Inizia un tour rapido per imparare a creare e inviare il tuo primo documento.',
|
||||
start_tour: 'Inizia il tour'
|
||||
start_tour: 'Inizia il tour',
|
||||
or_add_from: 'O aggiungi da',
|
||||
sync: 'Sincronizza',
|
||||
syncing: 'Sincronizzazione...'
|
||||
}
|
||||
|
||||
const pt = {
|
||||
view: 'Visualizar',
|
||||
payment_link: 'Link de pagamento',
|
||||
strikeout: 'Tachado',
|
||||
draw_strikethrough_the_document: 'Desenhe uma linha de tachado no documento',
|
||||
@@ -719,10 +732,14 @@ const pt = {
|
||||
and: 'e',
|
||||
or: 'ou',
|
||||
start_a_quick_tour_to_learn_how_to_create_an_send_your_first_document: 'Comece um tour rápido para aprender a criar e enviar seu primeiro documento.',
|
||||
start_tour: 'Iniciar tour'
|
||||
start_tour: 'Iniciar tour',
|
||||
or_add_from: 'Ou adicionar de',
|
||||
sync: 'Sincronizar',
|
||||
syncing: 'Sincronizando...'
|
||||
}
|
||||
|
||||
const fr = {
|
||||
view: 'Vue',
|
||||
payment_link: 'Lien de paiement',
|
||||
strikeout: 'Barrer',
|
||||
draw_strikethrough_the_document: 'Tracer une ligne de suppression sur le document',
|
||||
@@ -900,10 +917,14 @@ const fr = {
|
||||
and: 'et',
|
||||
or: 'ou',
|
||||
start_a_quick_tour_to_learn_how_to_create_an_send_your_first_document: 'Lancez une visite rapide pour apprendre à créer et envoyer votre premier document.',
|
||||
start_tour: 'Démarrer'
|
||||
start_tour: 'Démarrer',
|
||||
or_add_from: 'Ou ajouter depuis',
|
||||
sync: 'Synchroniser',
|
||||
syncing: 'Synchronisation...'
|
||||
}
|
||||
|
||||
const de = {
|
||||
view: 'Ansicht',
|
||||
payment_link: 'Zahlungslink',
|
||||
strikeout: 'Streichung',
|
||||
draw_strikethrough_the_document: 'Ziehe eine Streichung auf das Dokument',
|
||||
@@ -1081,10 +1102,14 @@ const de = {
|
||||
and: 'und',
|
||||
or: 'oder',
|
||||
start_a_quick_tour_to_learn_how_to_create_an_send_your_first_document: 'Starte eine kurze Tour, um zu lernen, wie du dein erstes Dokument erstellst und versendest.',
|
||||
start_tour: 'Starten'
|
||||
start_tour: 'Starten',
|
||||
or_add_from: 'Oder hinzufügen von',
|
||||
sync: 'Synchronisieren',
|
||||
syncing: 'Synchronisierung...'
|
||||
}
|
||||
|
||||
const nl = {
|
||||
view: 'Bekijken',
|
||||
payment_link: 'Betaallink',
|
||||
strikeout: 'Doorhalen',
|
||||
draw_strikethrough_the_document: 'Teken doorhaling in het document',
|
||||
@@ -1262,7 +1287,10 @@ const nl = {
|
||||
and: 'en',
|
||||
or: 'of',
|
||||
start_a_quick_tour_to_learn_how_to_create_an_send_your_first_document: 'Start een korte rondleiding om te leren hoe u uw eerste document maakt en verzendt',
|
||||
start_tour: 'Rondleiding starten'
|
||||
start_tour: 'Rondleiding starten',
|
||||
or_add_from: 'Of toevoegen van',
|
||||
sync: 'Synchroniseren',
|
||||
syncing: 'Synchroniseren...'
|
||||
}
|
||||
|
||||
export { en, es, it, pt, fr, de, nl }
|
||||
|
||||
@@ -94,12 +94,19 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex pb-2 pt-1.5 document-preview-name">
|
||||
<div class="flex items-center gap-1 pb-2 pt-1.5 document-preview-name">
|
||||
<GoogleDriveDocumentSettings
|
||||
v-if="item.google_drive_file_id"
|
||||
:template-id="template.id"
|
||||
:google-drive-file-id="item.google_drive_file_id"
|
||||
@success="$emit('replace', { replaceSchemaItem: item, ...$event })"
|
||||
/>
|
||||
<Contenteditable
|
||||
:model-value="item.name"
|
||||
:icon-width="16"
|
||||
:icon-inline="true"
|
||||
:hide-icon="false"
|
||||
:editable="editable"
|
||||
style="max-width: 95%"
|
||||
class="mx-auto"
|
||||
@update:model-value="onUpdateName"
|
||||
/>
|
||||
@@ -123,6 +130,7 @@ import Upload from './upload'
|
||||
import { IconRouteAltLeft, IconSortDescending2 } from '@tabler/icons-vue'
|
||||
import ConditionsModal from './conditions_modal'
|
||||
import ReplaceButton from './replace'
|
||||
import GoogleDriveDocumentSettings from './google_drive_document_settings'
|
||||
import Field from './field'
|
||||
import FieldType from './field_type'
|
||||
|
||||
@@ -133,6 +141,7 @@ export default {
|
||||
IconRouteAltLeft,
|
||||
ConditionsModal,
|
||||
ReplaceButton,
|
||||
GoogleDriveDocumentSettings,
|
||||
IconSortDescending2
|
||||
},
|
||||
inject: ['t'],
|
||||
|
||||
@@ -3,29 +3,155 @@
|
||||
<label
|
||||
id="add_document_button"
|
||||
:for="inputId"
|
||||
class="btn btn-outline w-full add-document-button"
|
||||
class="btn btn-outline w-full add-document-button px-0"
|
||||
:class="{ 'btn-disabled': isLoading }"
|
||||
>
|
||||
<IconInnerShadowTop
|
||||
v-if="isLoading"
|
||||
width="20"
|
||||
class="animate-spin"
|
||||
/>
|
||||
<IconUpload
|
||||
v-else
|
||||
width="20"
|
||||
/>
|
||||
<span v-if="isLoading">
|
||||
{{ t('uploading_') }}
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ t('add_document') }}
|
||||
</span>
|
||||
<div
|
||||
class="flex items-center justify-between w-full h-full"
|
||||
>
|
||||
<span
|
||||
class="flex items-center space-x-2 w-full justify-center"
|
||||
:class="{ 'pl-3': withGoogleDrive }"
|
||||
>
|
||||
<IconInnerShadowTop
|
||||
v-if="isLoading"
|
||||
width="20"
|
||||
class="animate-spin"
|
||||
/>
|
||||
<IconUpload
|
||||
v-else
|
||||
width="20"
|
||||
/>
|
||||
<span v-if="isLoading">
|
||||
{{ t('uploading_') }}
|
||||
</span>
|
||||
<span
|
||||
v-else
|
||||
class="mr-1 whitespace-nowrap truncate"
|
||||
>
|
||||
{{ t('add_document') }}
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
v-if="withGoogleDrive"
|
||||
class="dropdown dropdown-end dropdown-top inline h-full"
|
||||
style="width: 33px"
|
||||
>
|
||||
<label
|
||||
tabindex="0"
|
||||
class="flex items-center h-full cursor-pointer"
|
||||
>
|
||||
<IconChevronDown class="w-5 h-5 flex-shrink-0" />
|
||||
</label>
|
||||
<ul
|
||||
tabindex="0"
|
||||
:style="{ backgroundColor }"
|
||||
class="dropdown-content p-2 mt-2 shadow menu text-base mb-1 rounded-box text-right !text-base-content"
|
||||
>
|
||||
<li>
|
||||
<button
|
||||
type="button"
|
||||
@click="openGoogleDriveModal"
|
||||
>
|
||||
<IconBrandGoogleDrive class="w-5 h-5 flex-shrink-0" />
|
||||
<span class="whitespace-nowrap text-sm normal-case font-medium">Google Drive</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
<Teleport
|
||||
v-if="showGoogleDriveModal"
|
||||
:to="modalContainerEl"
|
||||
>
|
||||
<div
|
||||
class="modal modal-open items-start !animate-none overflow-y-auto"
|
||||
>
|
||||
<div
|
||||
class="absolute top-0 bottom-0 right-0 left-0"
|
||||
@click.prevent="showGoogleDriveModal = false"
|
||||
/>
|
||||
<div class="modal-box pt-4 pb-6 px-6 mt-20 max-h-none w-full max-w-xl">
|
||||
<div class="flex justify-between items-center border-b pb-2 mb-2 font-medium">
|
||||
<span class="modal-title">
|
||||
Google Drive
|
||||
</span>
|
||||
<a
|
||||
href="#"
|
||||
class="text-xl modal-close-button"
|
||||
@click.prevent="showGoogleDriveModal = false"
|
||||
>×</a>
|
||||
</div>
|
||||
<div>
|
||||
<form
|
||||
v-if="showGoogleDriveOauthButton"
|
||||
method="post"
|
||||
:action="googleDriveOauthPath"
|
||||
@submit="isConnectGoogleDriveClicked = true"
|
||||
>
|
||||
<input
|
||||
type="hidden"
|
||||
name="authenticity_token"
|
||||
:value="authenticityToken"
|
||||
autocomplete="off"
|
||||
>
|
||||
<button
|
||||
id="gdrive_oauth_button"
|
||||
class="btn bg-white btn-outline w-full text-base font-medium mt-4"
|
||||
data-turbo="false"
|
||||
type="submit"
|
||||
:disabled="isConnectGoogleDriveClicked"
|
||||
>
|
||||
<span v-if="isConnectGoogleDriveClicked">
|
||||
<span class="flex items-center justify-center space-x-2">
|
||||
<IconInnerShadowTop class="animate-spin" />
|
||||
<span class="">Submitting...</span>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
v-else
|
||||
>
|
||||
<span class="flex items-center justify-center space-x-2">
|
||||
<IconBrandGoogleDrive />
|
||||
<span>Connect Google Drive</span>
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</form>
|
||||
<div
|
||||
v-else
|
||||
class="relative"
|
||||
>
|
||||
<iframe
|
||||
class="border border-base-300 rounded-lg"
|
||||
style="width: 100%; height: 440px; background: white;"
|
||||
src="/template_google_drive"
|
||||
/>
|
||||
<div v-if="isLoadingGoogleDrive">
|
||||
<div
|
||||
class="bg-white absolute top-0 bottom-0 left-0 right-0 opacity-80 rounded-lg"
|
||||
style="margin: 1px"
|
||||
/>
|
||||
<div class="absolute top-0 bottom-0 left-0 right-0 flex items-center justify-center">
|
||||
<IconInnerShadowTop class="animate-spin" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Teleport>
|
||||
<form
|
||||
ref="form"
|
||||
class="hidden"
|
||||
>
|
||||
<input
|
||||
v-for="file in googleDriveFiles"
|
||||
:key="file.id"
|
||||
name="google_drive_file_ids[]"
|
||||
:value="file.id"
|
||||
>
|
||||
<input
|
||||
:id="inputId"
|
||||
ref="input"
|
||||
@@ -40,20 +166,32 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { IconUpload, IconInnerShadowTop } from '@tabler/icons-vue'
|
||||
import { IconUpload, IconInnerShadowTop, IconChevronDown, IconBrandGoogleDrive } from '@tabler/icons-vue'
|
||||
|
||||
export default {
|
||||
name: 'DocumentsUpload',
|
||||
components: {
|
||||
IconUpload,
|
||||
IconInnerShadowTop
|
||||
IconInnerShadowTop,
|
||||
IconChevronDown,
|
||||
IconBrandGoogleDrive
|
||||
},
|
||||
inject: ['baseFetch', 't'],
|
||||
inject: ['baseFetch', 't', 'backgroundColor'],
|
||||
props: {
|
||||
templateId: {
|
||||
type: [Number, String],
|
||||
required: true
|
||||
},
|
||||
authenticityToken: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: ''
|
||||
},
|
||||
withGoogleDrive: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
acceptFileTypes: {
|
||||
type: String,
|
||||
required: false,
|
||||
@@ -63,22 +201,85 @@ export default {
|
||||
emits: ['success', 'error'],
|
||||
data () {
|
||||
return {
|
||||
isLoading: false
|
||||
isLoading: false,
|
||||
isConnectGoogleDriveClicked: false,
|
||||
isLoadingGoogleDrive: false,
|
||||
googleDriveFiles: [],
|
||||
showGoogleDriveModal: false,
|
||||
showGoogleDriveOauthButton: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
inputId () {
|
||||
return 'el' + Math.random().toString(32).split('.')[1]
|
||||
},
|
||||
uploadUrl () {
|
||||
return `/templates/${this.templateId}/documents`
|
||||
queryParams () {
|
||||
return new URLSearchParams(window.location.search)
|
||||
},
|
||||
googleDriveOauthPath () {
|
||||
const params = {
|
||||
access_type: 'offline',
|
||||
include_granted_scopes: 'true',
|
||||
prompt: 'consent',
|
||||
scope: [
|
||||
'https://www.googleapis.com/auth/userinfo.email',
|
||||
'https://www.googleapis.com/auth/drive.file'
|
||||
].join(' '),
|
||||
state: new URLSearchParams({
|
||||
redir: `/templates/${this.templateId}/edit?google_drive_open=1`
|
||||
}).toString()
|
||||
}
|
||||
|
||||
const query = new URLSearchParams(params).toString()
|
||||
|
||||
return `/auth/google_oauth2?${query}`
|
||||
},
|
||||
modalContainerEl () {
|
||||
return this.$el.getRootNode().querySelector('#docuseal_modal_container')
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
window.addEventListener('message', this.messageHandler)
|
||||
|
||||
if (this.queryParams.get('google_drive_open') === '1') {
|
||||
this.openGoogleDriveModal()
|
||||
|
||||
window.history.replaceState({}, document.title, window.location.pathname)
|
||||
}
|
||||
},
|
||||
beforeUnmount () {
|
||||
window.removeEventListener('message', this.messageHandler)
|
||||
},
|
||||
methods: {
|
||||
async upload () {
|
||||
openGoogleDriveModal () {
|
||||
this.showGoogleDriveModal = true
|
||||
this.isLoadingGoogleDrive = true
|
||||
},
|
||||
messageHandler (event) {
|
||||
if (event.data.type === 'google-drive-files-picked') {
|
||||
this.googleDriveFiles = event.data.files || []
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.isLoadingGoogleDrive = true
|
||||
|
||||
this.upload({ path: `/templates/${this.templateId}/google_drive_documents` }).then((resp) => {
|
||||
if (resp.ok) {
|
||||
this.showGoogleDriveModal = false
|
||||
}
|
||||
}).finally(() => {
|
||||
this.isLoadingGoogleDrive = false
|
||||
})
|
||||
})
|
||||
} else if (event.data.type === 'google-drive-picker-loaded') {
|
||||
this.isLoadingGoogleDrive = false
|
||||
} else if (event.data.type === 'google-drive-picker-request-oauth') {
|
||||
this.showGoogleDriveOauthButton = true
|
||||
}
|
||||
},
|
||||
async upload ({ path } = {}) {
|
||||
this.isLoading = true
|
||||
|
||||
this.baseFetch(this.uploadUrl, {
|
||||
return this.baseFetch(path || `/templates/${this.templateId}/documents`, {
|
||||
method: 'POST',
|
||||
headers: { Accept: 'application/json' },
|
||||
body: new FormData(this.$refs.form)
|
||||
@@ -111,6 +312,10 @@ export default {
|
||||
this.isLoading = false
|
||||
}
|
||||
})
|
||||
} else if (data.status === 'google_drive_file_missing') {
|
||||
alert(data.error)
|
||||
this.$emit('error', data.error)
|
||||
this.isLoading = false
|
||||
} else {
|
||||
this.$emit('error', data.error)
|
||||
this.isLoading = false
|
||||
@@ -122,6 +327,8 @@ export default {
|
||||
this.isLoading = false
|
||||
})
|
||||
}
|
||||
|
||||
return resp
|
||||
}).catch(() => {
|
||||
this.isLoading = false
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user