Add initial work on new S3 upload component and button action

This commit is contained in:
Andrew Kingston 2022-01-11 14:01:21 +00:00
parent 4ccf3f9687
commit 8772dc342d
13 changed files with 231 additions and 1 deletions

View File

@ -44,7 +44,8 @@
"relationshipfield", "relationshipfield",
"daterangepicker", "daterangepicker",
"multifieldselect", "multifieldselect",
"jsonfield" "jsonfield",
"s3upload"
] ]
}, },
{ {

View File

@ -0,0 +1,33 @@
<script>
import { Select, Label } from "@budibase/bbui"
import { currentAsset } from "builderStore"
import { findAllMatchingComponents } from "builderStore/componentUtils"
export let parameters
$: components = findAllMatchingComponents($currentAsset.props, component =>
component._component.endsWith("s3upload")
)
</script>
<div class="root">
<Label small>S3 Upload Component</Label>
<Select
bind:value={parameters.componentId}
options={components}
getOptionLabel={x => x._instanceName}
getOptionValue={x => x._id}
/>
</div>
<style>
.root {
display: grid;
column-gap: var(--spacing-l);
row-gap: var(--spacing-s);
grid-template-columns: 120px 1fr;
align-items: center;
max-width: 400px;
margin: 0 auto;
}
</style>

View File

@ -11,3 +11,4 @@ export { default as ChangeFormStep } from "./ChangeFormStep.svelte"
export { default as UpdateState } from "./UpdateState.svelte" export { default as UpdateState } from "./UpdateState.svelte"
export { default as RefreshDataProvider } from "./RefreshDataProvider.svelte" export { default as RefreshDataProvider } from "./RefreshDataProvider.svelte"
export { default as DuplicateRow } from "./DuplicateRow.svelte" export { default as DuplicateRow } from "./DuplicateRow.svelte"
export { default as S3Upload } from "./S3Upload.svelte"

View File

@ -70,6 +70,10 @@
"name": "Update State", "name": "Update State",
"component": "UpdateState", "component": "UpdateState",
"dependsOnFeature": "state" "dependsOnFeature": "state"
},
{
"name": "Upload File to S3",
"component": "S3Upload"
} }
] ]
} }

View File

@ -0,0 +1,15 @@
<script>
import { Select } from "@budibase/bbui"
import { datasources } from "stores/backend"
export let value = null
$: dataSources = $datasources.list
.filter(ds => ds.source === "S3")
.map(ds => ({
label: ds.name,
value: ds._id,
}))
</script>
<Select options={dataSources} {value} on:change />

View File

@ -1,5 +1,6 @@
import { Checkbox, Select, Stepper } from "@budibase/bbui" import { Checkbox, Select, Stepper } from "@budibase/bbui"
import DataSourceSelect from "./DataSourceSelect.svelte" import DataSourceSelect from "./DataSourceSelect.svelte"
import S3DataSourceSelect from "./S3DataSourceSelect.svelte"
import DataProviderSelect from "./DataProviderSelect.svelte" import DataProviderSelect from "./DataProviderSelect.svelte"
import ButtonActionEditor from "./ButtonActionEditor/ButtonActionEditor.svelte" import ButtonActionEditor from "./ButtonActionEditor/ButtonActionEditor.svelte"
import TableSelect from "./TableSelect.svelte" import TableSelect from "./TableSelect.svelte"
@ -21,6 +22,7 @@ const componentMap = {
text: DrawerBindableCombobox, text: DrawerBindableCombobox,
select: Select, select: Select,
dataSource: DataSourceSelect, dataSource: DataSourceSelect,
"dataSource/s3": S3DataSourceSelect,
dataProvider: DataProviderSelect, dataProvider: DataProviderSelect,
boolean: Checkbox, boolean: Checkbox,
number: Stepper, number: Stepper,

View File

@ -3315,5 +3315,44 @@
"suffix": "repeater" "suffix": "repeater"
} }
] ]
},
"s3upload": {
"name": "S3 File Upload",
"icon": "UploadToCloud",
"styles": ["size"],
"editable": true,
"settings": [
{
"type": "field/attachment",
"label": "Field",
"key": "field"
},
{
"type": "text",
"label": "Label",
"key": "label"
},
{
"type": "dataSource/s3",
"label": "S3 Datasource",
"key": "datasource"
},
{
"type": "text",
"label": "Bucket",
"key": "bucket"
},
{
"type": "boolean",
"label": "Disabled",
"key": "disabled",
"defaultValue": false
},
{
"type": "validation/attachment",
"label": "Validation",
"key": "validation"
}
]
} }
} }

View File

@ -0,0 +1,79 @@
<script>
import Field from "./Field.svelte"
import { CoreDropzone } from "@budibase/bbui"
import { getContext, onMount, onDestroy } from "svelte"
export let dataSource
export let bucket
export let field
export let label
export let disabled = false
export let validation
let fieldState
let fieldApi
const { API, notificationStore, uploadStore } = getContext("sdk")
const component = getContext("component")
const formContext = getContext("form")
// 5GB cap per item sent via S3 REST API
const MaxFileSize = 1000000000 * 5
let file
const handleFileTooLarge = () => {
notificationStore.actions.warning(
"Files cannot exceed 5GB. Please try again with a smaller file."
)
}
const processFiles = async fileList => {
// let data = new FormData()
// for (let i = 0; i < fileList.length; i++) {
// data.append("file", fileList[i])
// }
// return await API.uploadAttachment(data, formContext?.dataSource?.tableId)
file = fileList[0]
console.log("processing", fileList)
return []
}
const upload = async () => {
console.log("UPLOADING!!!")
}
onMount(() => {
uploadStore.actions.registerFileUpload($component.id, upload)
})
onDestroy(() => {
uploadStore.actions.unregisterFileUpload($component.id)
})
</script>
<Field
{label}
{field}
{disabled}
{validation}
type="s3upload"
bind:fieldState
bind:fieldApi
defaultValue={[]}
>
{#if fieldState}
<CoreDropzone
value={fieldState.value}
disabled={fieldState.disabled}
error={fieldState.error}
on:change={e => {
fieldApi.setValue(e.detail)
}}
{processFiles}
{handleFileTooLarge}
maximum={1}
fileSizeLimit={MaxFileSize}
/>
{/if}
</Field>

View File

@ -12,3 +12,4 @@ export { default as relationshipfield } from "./RelationshipField.svelte"
export { default as passwordfield } from "./PasswordField.svelte" export { default as passwordfield } from "./PasswordField.svelte"
export { default as formstep } from "./FormStep.svelte" export { default as formstep } from "./FormStep.svelte"
export { default as jsonfield } from "./JSONField.svelte" export { default as jsonfield } from "./JSONField.svelte"
export { default as s3upload } from "./S3Upload.svelte"

View File

@ -5,6 +5,7 @@ import {
routeStore, routeStore,
screenStore, screenStore,
builderStore, builderStore,
uploadStore,
} from "stores" } from "stores"
import { styleable } from "utils/styleable" import { styleable } from "utils/styleable"
import { linkable } from "utils/linkable" import { linkable } from "utils/linkable"
@ -19,6 +20,7 @@ export default {
routeStore, routeStore,
screenStore, screenStore,
builderStore, builderStore,
uploadStore,
styleable, styleable,
linkable, linkable,
getAction, getAction,

View File

@ -9,6 +9,7 @@ export { confirmationStore } from "./confirmation"
export { peekStore } from "./peek" export { peekStore } from "./peek"
export { stateStore } from "./state" export { stateStore } from "./state"
export { themeStore } from "./theme" export { themeStore } from "./theme"
export { uploadStore } from "./uploads.js"
// Context stores are layered and duplicated, so it is not a singleton // Context stores are layered and duplicated, so it is not a singleton
export { createContextStore } from "./context" export { createContextStore } from "./context"

View File

@ -0,0 +1,42 @@
import { writable, get } from "svelte/store"
export const createUploadStore = () => {
const store = writable([])
// Registers a new file upload component
const registerFileUpload = (componentId, callback) => {
if (!componentId || !callback) {
return
}
store.update(state => {
state.push({
componentId,
callback,
})
return state
})
}
// Unregisters a file upload component
const unregisterFileUpload = componentId => {
store.update(state => state.filter(c => c.componentId !== componentId))
}
// Processes a file upload for a given component ID
const processFileUpload = async componentId => {
if (!componentId) {
return
}
const component = get(store).find(c => c.componentId === componentId)
await component?.callback()
}
return {
subscribe: store.subscribe,
actions: { registerFileUpload, unregisterFileUpload, processFileUpload },
}
}
export const uploadStore = createUploadStore()

View File

@ -5,6 +5,7 @@ import {
confirmationStore, confirmationStore,
authStore, authStore,
stateStore, stateStore,
uploadStore,
} from "stores" } from "stores"
import { saveRow, deleteRow, executeQuery, triggerAutomation } from "api" import { saveRow, deleteRow, executeQuery, triggerAutomation } from "api"
import { ActionTypes } from "constants" import { ActionTypes } from "constants"
@ -157,6 +158,14 @@ const updateStateHandler = action => {
} }
} }
const s3UploadHandler = async action => {
const { componentId } = action.parameters
if (!componentId) {
return
}
await uploadStore.actions.processFileUpload(componentId)
}
const handlerMap = { const handlerMap = {
["Save Row"]: saveRowHandler, ["Save Row"]: saveRowHandler,
["Duplicate Row"]: duplicateRowHandler, ["Duplicate Row"]: duplicateRowHandler,
@ -171,6 +180,7 @@ const handlerMap = {
["Close Screen Modal"]: closeScreenModalHandler, ["Close Screen Modal"]: closeScreenModalHandler,
["Change Form Step"]: changeFormStepHandler, ["Change Form Step"]: changeFormStepHandler,
["Update State"]: updateStateHandler, ["Update State"]: updateStateHandler,
["Upload File to S3"]: s3UploadHandler,
} }
const confirmTextMap = { const confirmTextMap = {