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
$: if (value) {
const [attachment] = value || []
signatureFile = attachment
signatureFile = value
}
$: if (signatureFile?.url) {

View File

@ -358,6 +358,7 @@
value.customType !== "cron" &&
value.customType !== "triggerSchema" &&
value.customType !== "automationFields" &&
value.type !== "signature" &&
value.type !== "attachment" &&
value.type !== "attachment_single"
)
@ -449,7 +450,7 @@
value={inputData[key]}
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="label-wrapper">
<Label>{label}</Label>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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