Refactored signature from previous array implementation to the new single approach. Added support for automations

This commit is contained in:
Dean 2024-05-13 16:19:02 +01:00
parent f696299ceb
commit 0589a6a730
20 changed files with 82 additions and 84 deletions

View File

@ -66,8 +66,7 @@
let urlFailed let urlFailed
$: if (value) { $: if (value) {
const [attachment] = value || [] signatureFile = value
signatureFile = attachment
} }
$: if (signatureFile?.url) { $: if (signatureFile?.url) {

View File

@ -358,6 +358,7 @@
value.customType !== "cron" && value.customType !== "cron" &&
value.customType !== "triggerSchema" && value.customType !== "triggerSchema" &&
value.customType !== "automationFields" && value.customType !== "automationFields" &&
value.type !== "signature" &&
value.type !== "attachment" && value.type !== "attachment" &&
value.type !== "attachment_single" value.type !== "attachment_single"
) )
@ -449,7 +450,7 @@
value={inputData[key]} value={inputData[key]}
options={Object.keys(table?.schema || {})} options={Object.keys(table?.schema || {})}
/> />
{:else if value.type === "attachment"} {:else if value.type === "attachment" || value.type === "signature"}
<div class="attachment-field-wrapper"> <div class="attachment-field-wrapper">
<div class="label-wrapper"> <div class="label-wrapper">
<Label>{label}</Label> <Label>{label}</Label>

View File

@ -24,6 +24,11 @@
let table let table
let schemaFields let schemaFields
let attachmentTypes = [
FieldType.ATTACHMENTS,
FieldType.ATTACHMENT_SINGLE,
FieldType.SIGNATURE,
]
$: { $: {
table = $tables.list.find(table => table._id === value?.tableId) table = $tables.list.find(table => table._id === value?.tableId)
@ -120,15 +125,9 @@
{#if schemaFields.length} {#if schemaFields.length}
{#each schemaFields as [field, schema]} {#each schemaFields as [field, schema]}
{#if !schema.autocolumn} {#if !schema.autocolumn}
<div <div class:schema-fields={!attachmentTypes.includes(schema.type)}>
class:schema-fields={schema.type !== FieldType.ATTACHMENTS &&
schema.type !== FieldType.ATTACHMENT_SINGLE}
>
<Label>{field}</Label> <Label>{field}</Label>
<div <div class:field-width={!attachmentTypes.includes(schema.type)}>
class:field-width={schema.type !== FieldType.ATTACHMENTS &&
schema.type !== FieldType.ATTACHMENT_SINGLE}
>
{#if isTestModal} {#if isTestModal}
<RowSelectorTypes <RowSelectorTypes
{isTestModal} {isTestModal}

View File

@ -29,7 +29,8 @@
let params = {} let params = {}
if ( if (
schema.type === FieldType.ATTACHMENT_SINGLE && (schema.type === FieldType.ATTACHMENT_SINGLE ||
schema.type === FieldType.SIGNATURE) &&
Object.keys(keyValuObj).length === 0 Object.keys(keyValuObj).length === 0
) { ) {
return [] return []
@ -100,16 +101,20 @@
on:change={e => onChange(e, field)} on:change={e => onChange(e, field)}
useLabel={false} useLabel={false}
/> />
{:else if schema.type === FieldType.ATTACHMENTS || schema.type === FieldType.ATTACHMENT_SINGLE} {:else if schema.type === FieldType.ATTACHMENTS || schema.type === FieldType.ATTACHMENT_SINGLE || schema.type === FieldType.SIGNATURE}
<div class="attachment-field-spacinng"> <div class="attachment-field-spacinng">
<KeyValueBuilder <KeyValueBuilder
on:change={e => on:change={e =>
onChange( onChange(
{ {
detail: detail:
schema.type === FieldType.ATTACHMENT_SINGLE schema.type === FieldType.ATTACHMENT_SINGLE ||
schema.type === FieldType.SIGNATURE
? e.detail.length > 0 ? e.detail.length > 0
? { url: e.detail[0].name, filename: e.detail[0].value } ? {
url: e.detail[0].name,
filename: e.detail[0].value,
}
: {} : {}
: e.detail.map(({ name, value }) => ({ : e.detail.map(({ name, value }) => ({
url: name, url: name,
@ -125,7 +130,8 @@
customButtonText={"Add attachment"} customButtonText={"Add attachment"}
keyPlaceholder={"URL"} keyPlaceholder={"URL"}
valuePlaceholder={"Filename"} valuePlaceholder={"Filename"}
actionButtonDisabled={schema.type === FieldType.ATTACHMENT_SINGLE && actionButtonDisabled={(schema.type === FieldType.ATTACHMENT_SINGLE ||
schema.type === FieldType.SIGNATURE) &&
Object.keys(value[field]).length >= 1} Object.keys(value[field]).length >= 1}
/> />
</div> </div>

View File

@ -62,7 +62,8 @@
try { try {
const uploadReq = await API.uploadBuilderAttachment(attachRequest) const uploadReq = await API.uploadBuilderAttachment(attachRequest)
value = uploadReq const [signatureAttachment] = uploadReq
value = signatureAttachment
} catch (error) { } catch (error) {
$notifications.error(error.message || "Failed to save signature") $notifications.error(error.message || "Failed to save signature")
value = [] value = []
@ -114,14 +115,14 @@
{:else if type === "signature"} {:else if type === "signature"}
<div class="signature"> <div class="signature">
<Label>{label}</Label> <Label>{label}</Label>
<div class="sig-wrap" class:display={value?.length}> <div class="sig-wrap" class:display={value}>
{#if value?.length} {#if value}
<CoreSignature <CoreSignature
{darkMode} {darkMode}
{value} {value}
editable={false} editable={false}
on:clear={() => { on:clear={() => {
value = [] value = null
}} }}
/> />
{:else} {:else}

View File

@ -54,6 +54,10 @@
label: "Attachment", label: "Attachment",
value: FieldType.ATTACHMENT_SINGLE, value: FieldType.ATTACHMENT_SINGLE,
}, },
{
label: "Signature",
value: FieldType.SIGNATURE,
},
{ {
label: "Attachment list", label: "Attachment list",
value: FieldType.ATTACHMENTS, value: FieldType.ATTACHMENTS,

View File

@ -28,6 +28,12 @@
let bindingDrawer let bindingDrawer
let currentVal = value let currentVal = value
let attachmentTypes = [
FieldType.ATTACHMENT_SINGLE,
FieldType.ATTACHMENTS,
FieldType.SIGNATURE,
]
$: readableValue = runtimeToReadableBinding(bindings, value) $: readableValue = runtimeToReadableBinding(bindings, value)
$: tempValue = readableValue $: tempValue = readableValue
$: isJS = isJSBinding(value) $: isJS = isJSBinding(value)
@ -105,6 +111,7 @@
boolean: isValidBoolean, boolean: isValidBoolean,
attachment: false, attachment: false,
attachment_single: false, attachment_single: false,
signature: false,
} }
const isValid = value => { const isValid = value => {
@ -126,6 +133,7 @@
"bigint", "bigint",
"barcodeqr", "barcodeqr",
"attachment", "attachment",
"signature",
"attachment_single", "attachment_single",
].includes(type) ].includes(type)
) { ) {
@ -169,7 +177,7 @@
{updateOnChange} {updateOnChange}
/> />
{/if} {/if}
{#if !disabled && type !== "formula" && !disabled && type !== FieldType.ATTACHMENTS && !disabled && type !== FieldType.ATTACHMENT_SINGLE} {#if !disabled && type !== "formula" && !disabled && !attachmentTypes.includes(type)}
<div <div
class={`icon ${getIconClass(value, type)}`} class={`icon ${getIconClass(value, type)}`}
on:click={() => { on:click={() => {

View File

@ -132,7 +132,6 @@ export const FIELDS = {
type: FieldType.SIGNATURE, type: FieldType.SIGNATURE,
icon: "AnnotatePen", icon: "AnnotatePen",
constraints: { constraints: {
type: "array",
presence: false, presence: false,
}, },
}, },

View File

@ -35,10 +35,10 @@
data: attachRequest, data: attachRequest,
tableId: formContext?.dataSource?.tableId, tableId: formContext?.dataSource?.tableId,
}) })
const [signatureAttachment] = resp
updateValue = resp updateValue = signatureAttachment
} else { } else {
updateValue = [] updateValue = null
} }
const changed = fieldApi.setValue(updateValue) const changed = fieldApi.setValue(updateValue)
@ -54,9 +54,9 @@
} }
const deleteSignature = async () => { const deleteSignature = async () => {
const changed = fieldApi.setValue([]) const changed = fieldApi.setValue(null)
if (onChange && changed) { if (onChange && changed) {
onChange({ value: [] }) onChange({ value: null })
} }
} }

View File

@ -192,16 +192,16 @@ const parseType = (value, type) => {
return value === true return value === true
} }
// Parse attachments/signatures, treating no elements as null // Parse attachments, treating no elements as null
if (type === FieldTypes.ATTACHMENTS || type === FieldTypes.SIGNATURE) { if (type === FieldTypes.ATTACHMENTS) {
if (!Array.isArray(value) || !value.length) { if (!Array.isArray(value) || !value.length) {
return null return null
} }
return value return value
} }
// Parse attachment single, treating no key as null // Parse attachment/signature single, treating no key as null
if (type === FieldTypes.ATTACHMENT_SINGLE) { if (type === FieldTypes.ATTACHMENT_SINGLE || type === FieldTypes.SIGNATURE) {
if (!value?.key) { if (!value?.key) {
return null return null
} }

View File

@ -15,14 +15,8 @@
const { API, notifications, props } = getContext("grid") const { API, notifications, props } = getContext("grid")
let isOpen = false let isOpen = false
let signature
let modal let modal
$: if (value) {
const [attachment] = value
signature = attachment
}
$: editable = focused && !readonly $: editable = focused && !readonly
$: { $: {
if (!focused) { if (!focused) {
@ -43,7 +37,7 @@
} }
const deleteSignature = async () => { const deleteSignature = async () => {
onChange([]) onChange(null)
} }
const saveSignature = async sigCanvas => { const saveSignature = async sigCanvas => {
@ -54,7 +48,8 @@
try { try {
const uploadReq = await API.uploadBuilderAttachment(attachRequest) const uploadReq = await API.uploadBuilderAttachment(attachRequest)
onChange(uploadReq) const [signatureAttachment] = uploadReq
onChange(signatureAttachment)
} catch (error) { } catch (error) {
$notifications.error(error.message || "Failed to save signature") $notifications.error(error.message || "Failed to save signature")
return [] return []
@ -79,9 +74,9 @@
class:editable class:editable
on:click={editable ? open : null} on:click={editable ? open : null}
> >
{#if signature?.url} {#if value?.url}
<!-- svelte-ignore a11y-missing-attribute --> <!-- svelte-ignore a11y-missing-attribute -->
<img src={signature?.url} /> <img src={value?.url} />
{/if} {/if}
</div> </div>
@ -94,8 +89,8 @@
/> />
{#if isOpen} {#if isOpen}
<div class="signature" class:invertX class:invertY class:empty={!signature}> <div class="signature" class:invertX class:invertY class:empty={!value}>
{#if signature?.key} {#if value?.key}
<div class="signature-wrap"> <div class="signature-wrap">
<CoreSignature <CoreSignature
darkMode={$props.darkMode} darkMode={$props.darkMode}

View File

@ -312,7 +312,7 @@ describe.each([
const signature: FieldSchema = { const signature: FieldSchema = {
type: FieldType.SIGNATURE, type: FieldType.SIGNATURE,
name: "signature", name: "signature",
constraints: { type: "array", presence: false }, constraints: { presence: false },
} }
const bool: FieldSchema = { const bool: FieldSchema = {
type: FieldType.BOOLEAN, type: FieldType.BOOLEAN,
@ -382,8 +382,6 @@ describe.each([
attachmentListEmptyArrayStr: attachmentList, attachmentListEmptyArrayStr: attachmentList,
signatureNull: signature, signatureNull: signature,
signatureUndefined: signature, signatureUndefined: signature,
signatureEmpty: signature,
signatureEmptyArrayStr: signature,
arrayFieldEmptyArrayStr: arrayField, arrayFieldEmptyArrayStr: arrayField,
arrayFieldArrayStrKnown: arrayField, arrayFieldArrayStrKnown: arrayField,
arrayFieldNull: arrayField, arrayFieldNull: arrayField,
@ -427,8 +425,6 @@ describe.each([
attachmentListEmptyArrayStr: "[]", attachmentListEmptyArrayStr: "[]",
signatureNull: null, signatureNull: null,
signatureUndefined: undefined, signatureUndefined: undefined,
signatureEmpty: "",
signatureEmptyArrayStr: "[]",
arrayFieldEmptyArrayStr: "[]", arrayFieldEmptyArrayStr: "[]",
arrayFieldUndefined: undefined, arrayFieldUndefined: undefined,
arrayFieldNull: null, arrayFieldNull: null,
@ -463,10 +459,8 @@ describe.each([
expect(row.attachmentListUndefined).toBe(undefined) expect(row.attachmentListUndefined).toBe(undefined)
expect(row.attachmentListEmpty).toEqual([]) expect(row.attachmentListEmpty).toEqual([])
expect(row.attachmentListEmptyArrayStr).toEqual([]) expect(row.attachmentListEmptyArrayStr).toEqual([])
expect(row.signatureNull).toEqual([]) expect(row.signatureNull).toEqual(null)
expect(row.signatureUndefined).toBe(undefined) expect(row.signatureUndefined).toBe(undefined)
expect(row.signatureEmpty).toEqual([])
expect(row.signatureEmptyArrayStr).toEqual([])
expect(row.arrayFieldEmptyArrayStr).toEqual([]) expect(row.arrayFieldEmptyArrayStr).toEqual([])
expect(row.arrayFieldNull).toEqual([]) expect(row.arrayFieldNull).toEqual([])
expect(row.arrayFieldUndefined).toEqual(undefined) expect(row.arrayFieldUndefined).toEqual(undefined)
@ -990,11 +984,11 @@ describe.each([
signature: { signature: {
type: FieldType.SIGNATURE, type: FieldType.SIGNATURE,
name: "signature", name: "signature",
constraints: { type: "array", presence: false }, constraints: { presence: false },
}, },
}, },
"signature", "signature",
[`${uuid.v4()}.png`] `${uuid.v4()}.png`
) )
}) })
}) })

View File

@ -113,7 +113,8 @@ export async function sendAutomationAttachmentsToStorage(
const schema = table.schema[prop] const schema = table.schema[prop]
if ( if (
schema?.type === FieldType.ATTACHMENTS || schema?.type === FieldType.ATTACHMENTS ||
schema?.type === FieldType.ATTACHMENT_SINGLE schema?.type === FieldType.ATTACHMENT_SINGLE ||
schema?.type === FieldType.SIGNATURE
) { ) {
attachmentRows[prop] = value attachmentRows[prop] = value
} }

View File

@ -61,15 +61,15 @@ export async function updateAttachmentColumns(prodAppId: string, db: Database) {
for (let column of columns) { for (let column of columns) {
const columnType = table.schema[column].type const columnType = table.schema[column].type
if ( if (
(columnType === FieldType.ATTACHMENTS || columnType === FieldType.ATTACHMENTS &&
columnType === FieldType.SIGNATURE) &&
Array.isArray(row[column]) Array.isArray(row[column])
) { ) {
row[column] = row[column].map((attachment: RowAttachment) => row[column] = row[column].map((attachment: RowAttachment) =>
rewriteAttachmentUrl(prodAppId, attachment) rewriteAttachmentUrl(prodAppId, attachment)
) )
} else if ( } else if (
columnType === FieldType.ATTACHMENT_SINGLE && (columnType === FieldType.ATTACHMENT_SINGLE ||
columnType === FieldType.SIGNATURE) &&
row[column] row[column]
) { ) {
row[column] = rewriteAttachmentUrl(prodAppId, row[column]) row[column] = rewriteAttachmentUrl(prodAppId, row[column])

View File

@ -181,15 +181,13 @@ export async function validate({
errors[fieldName] = [`${fieldName} is required`] errors[fieldName] = [`${fieldName} is required`]
} }
} else if ( } else if (
(type === FieldType.ATTACHMENTS || (type === FieldType.ATTACHMENTS || type === FieldType.JSON) &&
type === FieldType.JSON ||
type === FieldType.SIGNATURE) &&
typeof row[fieldName] === "string" typeof row[fieldName] === "string"
) { ) {
// this should only happen if there is an error // this should only happen if there is an error
try { try {
const json = JSON.parse(row[fieldName]) const json = JSON.parse(row[fieldName])
if (type === FieldType.ATTACHMENTS || type === FieldType.SIGNATURE) { if (type === FieldType.ATTACHMENTS) {
if (Array.isArray(json)) { if (Array.isArray(json)) {
row[fieldName] = json row[fieldName] = json
} else { } else {

View File

@ -94,14 +94,14 @@ describe("should be able to re-write attachment URLs", () => {
}, },
}, },
{ {
signature: [{ ...attachment }], signature: { ...attachment },
otherCol: "string", otherCol: "string",
} }
) )
for (const row of rows) { for (const row of rows) {
expect(row.otherCol).toBe("string") expect(row.otherCol).toBe("string")
expect(row.signature[0].url).toBe("") expect(row.signature.url).toBe("")
expect(row.signature[0].key).toBe(`${db.name}/attachments/a.png`) expect(row.signature.key).toBe(`${db.name}/attachments/a.png`)
} }
}) })
}) })

View File

@ -35,7 +35,7 @@ export class AttachmentCleanup {
return [] return []
} }
if (type === FieldType.ATTACHMENTS || type === FieldType.SIGNATURE) { if (type === FieldType.ATTACHMENTS) {
return rowData.map((attachment: any) => attachment.key) return rowData.map((attachment: any) => attachment.key)
} }
return [rowData.key] return [rowData.key]

View File

@ -148,17 +148,17 @@ export async function inputProcessing(
} }
// remove any attachment urls, they are generated on read // remove any attachment urls, they are generated on read
if ( if (field.type === FieldType.ATTACHMENTS) {
field.type === FieldType.ATTACHMENTS ||
field.type === FieldType.SIGNATURE
) {
const attachments = clonedRow[key] const attachments = clonedRow[key]
if (attachments?.length) { if (attachments?.length) {
attachments.forEach((attachment: RowAttachment) => { attachments.forEach((attachment: RowAttachment) => {
delete attachment.url delete attachment.url
}) })
} }
} else if (field.type === FieldType.ATTACHMENT_SINGLE) { } else if (
field.type === FieldType.ATTACHMENT_SINGLE ||
field.type === FieldType.SIGNATURE
) {
const attachment = clonedRow[key] const attachment = clonedRow[key]
if (attachment?.url) { if (attachment?.url) {
delete clonedRow[key].url delete clonedRow[key].url
@ -224,10 +224,7 @@ export async function outputProcessing<T extends Row[] | Row>(
// process complex types: attachements, bb references... // process complex types: attachements, bb references...
for (let [property, column] of Object.entries(table.schema)) { for (let [property, column] of Object.entries(table.schema)) {
if ( if (column.type === FieldType.ATTACHMENTS) {
column.type === FieldType.ATTACHMENTS ||
column.type === FieldType.SIGNATURE
) {
for (let row of enriched) { for (let row of enriched) {
if (row[property] == null || !Array.isArray(row[property])) { if (row[property] == null || !Array.isArray(row[property])) {
continue continue
@ -238,7 +235,10 @@ export async function outputProcessing<T extends Row[] | Row>(
} }
}) })
} }
} else if (column.type === FieldType.ATTACHMENT_SINGLE) { } else if (
column.type === FieldType.ATTACHMENT_SINGLE ||
column.type === FieldType.SIGNATURE
) {
for (let row of enriched) { for (let row of enriched) {
if (!row[property] || Object.keys(row[property]).length === 0) { if (!row[property] || Object.keys(row[property]).length === 0) {
continue continue

View File

@ -113,14 +113,6 @@ export const TYPE_TRANSFORM_MAP: any = {
[undefined]: undefined, [undefined]: undefined,
parse: parseArrayString, parse: parseArrayString,
}, },
//Review this
[FieldType.SIGNATURE]: {
//@ts-ignore
[null]: [],
//@ts-ignore
[undefined]: undefined,
parse: parseArrayString,
},
[FieldType.BOOLEAN]: { [FieldType.BOOLEAN]: {
"": null, "": null,
//@ts-ignore //@ts-ignore

View File

@ -149,7 +149,8 @@ export function parse(rows: Rows, schema: TableSchema): Rows {
} }
} else if ( } else if (
(columnType === FieldType.ATTACHMENTS || (columnType === FieldType.ATTACHMENTS ||
columnType === FieldType.ATTACHMENT_SINGLE) && columnType === FieldType.ATTACHMENT_SINGLE ||
columnType === FieldType.SIGNATURE) &&
typeof columnData === "string" typeof columnData === "string"
) { ) {
parsedRow[columnName] = parseCsvExport(columnData) parsedRow[columnName] = parseCsvExport(columnData)