Manually upload signature (#521)
* Manually upload signature * Signature upload UI changes * fix signature on clear --------- Co-authored-by: Julien Nahum <julien@nahum.net>
This commit is contained in:
parent
1ac71ecf8b
commit
5049ba7fb1
|
|
@ -216,6 +216,10 @@ class StoreFormSubmissionJob implements ShouldQueue
|
|||
|
||||
private function storeSignature(?string $value)
|
||||
{
|
||||
if ($value && preg_match('/^[\/\w\-. ]+$/', $value)) { // If it's filename
|
||||
return $this->storeFile($value);
|
||||
}
|
||||
|
||||
if ($value == null || !isset(explode(',', $value)[1])) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,42 @@
|
|||
<slot name="label" />
|
||||
</template>
|
||||
|
||||
<div
|
||||
v-if="loading || file"
|
||||
:class="[
|
||||
theme.SignatureInput.input,
|
||||
theme.SignatureInput.spacing.horizontal,
|
||||
theme.SignatureInput.spacing.vertical,
|
||||
theme.SignatureInput.fontSize,
|
||||
theme.SignatureInput.borderRadius,
|
||||
{
|
||||
'!ring-red-500 !ring-2 !border-transparent': hasError,
|
||||
'!cursor-not-allowed !bg-gray-200': disabled,
|
||||
},
|
||||
]"
|
||||
class="flex flex-wrap items-center justify-center gap-4"
|
||||
>
|
||||
<div
|
||||
v-if="loading"
|
||||
class="text-gray-600 dark:text-gray-400"
|
||||
>
|
||||
<Loader class="mx-auto h-6 w-6" />
|
||||
<p class="mt-2 text-center text-sm text-gray-500">
|
||||
Uploading your file...
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<uploaded-file
|
||||
v-else
|
||||
:key="file.url"
|
||||
:file="file"
|
||||
:theme="theme"
|
||||
:show-remove="false"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<VueSignaturePad
|
||||
v-else
|
||||
ref="signaturePad"
|
||||
:class="[
|
||||
theme.SignatureInput.input,
|
||||
|
|
@ -23,6 +58,26 @@
|
|||
/>
|
||||
|
||||
<template #bottom_after_help>
|
||||
<small
|
||||
v-if="!file"
|
||||
:class="theme.default.help"
|
||||
class="flex-auto"
|
||||
>
|
||||
<input
|
||||
ref="actual-input"
|
||||
class="hidden"
|
||||
:multiple="false"
|
||||
type="file"
|
||||
accept=".pdf,.png,.jpg,.jpeg"
|
||||
@change="manualFileUpload"
|
||||
>
|
||||
<a
|
||||
:class="theme.default.help"
|
||||
href="#"
|
||||
@click.prevent="openFileUpload"
|
||||
>Upload file instead</a>
|
||||
</small>
|
||||
|
||||
<small :class="theme.default.help">
|
||||
<a
|
||||
:class="theme.default.help"
|
||||
|
|
@ -42,6 +97,7 @@
|
|||
import { inputProps, useFormInput } from "./useFormInput.js"
|
||||
import InputWrapper from "./components/InputWrapper.vue"
|
||||
import { VueSignaturePad } from "vue-signature-pad"
|
||||
import { storeFile } from "~/lib/file-uploads.js"
|
||||
|
||||
export default {
|
||||
name: "SignatureInput",
|
||||
|
|
@ -57,12 +113,31 @@ export default {
|
|||
}
|
||||
},
|
||||
|
||||
data: () => ({
|
||||
file: null,
|
||||
loading: false
|
||||
}),
|
||||
|
||||
watch: {
|
||||
file: {
|
||||
handler(file) {
|
||||
this.compVal = file?.url || null
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
clear() {
|
||||
this.$refs.signaturePad.clearSignature()
|
||||
this.file = null
|
||||
this.$refs.signaturePad?.clearSignature()
|
||||
this.onEnd()
|
||||
},
|
||||
onEnd() {
|
||||
if (!this.$refs.signaturePad) {
|
||||
this.form[this.name] = null
|
||||
return
|
||||
}
|
||||
|
||||
if (this.disabled) {
|
||||
this.$refs.signaturePad.clearSignature()
|
||||
} else {
|
||||
|
|
@ -71,6 +146,39 @@ export default {
|
|||
this.form[this.name] = !isEmpty && data ? data : null
|
||||
}
|
||||
},
|
||||
openFileUpload() {
|
||||
if (this.disabled || !this.$refs['actual-input']) return
|
||||
this.$refs['actual-input'].click()
|
||||
},
|
||||
manualFileUpload(e) {
|
||||
const files = e.target.files
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
this.uploadFileToServer(files.item(i))
|
||||
}
|
||||
},
|
||||
uploadFileToServer(file) {
|
||||
if (this.disabled) return
|
||||
this.loading = true
|
||||
storeFile(file)
|
||||
.then((response) => {
|
||||
this.file = {
|
||||
file: file,
|
||||
url: file.name.split('.').slice(0, -1).join('.') + '_' + response.uuid + '.' + response.extension,
|
||||
src: this.getFileSrc(file)
|
||||
}
|
||||
this.loading = false
|
||||
})
|
||||
.catch((error) => {
|
||||
this.loading = false
|
||||
this.file = null
|
||||
})
|
||||
},
|
||||
getFileSrc(file) {
|
||||
if (file.type && file.type.split('/')[0] === 'image') {
|
||||
return URL.createObjectURL(file)
|
||||
}
|
||||
return null
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
{{ file.file.name }}
|
||||
</p>
|
||||
<a
|
||||
v-if="showRemove"
|
||||
href="javascript:void(0);"
|
||||
class="flex text-gray-400 rounded hover:bg-neutral-50 hover:text-red-500 dark:text-gray-600 p-1"
|
||||
role="button"
|
||||
|
|
@ -69,7 +70,8 @@ export default {
|
|||
name: "UploadedFile",
|
||||
|
||||
props: {
|
||||
file: { type:Object, default: null },
|
||||
file: { type: Object, default: null },
|
||||
showRemove: { type: Boolean, default: true },
|
||||
theme: {
|
||||
type: Object, default: () => {
|
||||
const theme = inject("theme", null)
|
||||
|
|
|
|||
Loading…
Reference in New Issue