Fix table imports
This commit is contained in:
parent
5bda7daf2f
commit
b8f27b9bf7
|
@ -1,14 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import {
|
import { ActionButton, Button, Body, notifications } from "@budibase/bbui"
|
||||||
ActionButton,
|
|
||||||
Label,
|
|
||||||
Button,
|
|
||||||
Body,
|
|
||||||
Layout,
|
|
||||||
notifications,
|
|
||||||
} from "@budibase/bbui"
|
|
||||||
import DetailPopover from "components/common/DetailPopover.svelte"
|
import DetailPopover from "components/common/DetailPopover.svelte"
|
||||||
import TableDataImport from "components/backend/TableNavigator/TableDataImport.svelte"
|
import ExistingTableDataImport from "components/backend/TableNavigator/ExistingTableDataImport.svelte"
|
||||||
import { createEventDispatcher } from "svelte"
|
import { createEventDispatcher } from "svelte"
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
|
|
||||||
|
@ -72,17 +65,14 @@
|
||||||
Import rows to an existing table from a CSV or JSON file. Only columns from
|
Import rows to an existing table from a CSV or JSON file. Only columns from
|
||||||
the file which exist in the table will be imported.
|
the file which exist in the table will be imported.
|
||||||
</Body>
|
</Body>
|
||||||
<Layout gap="XS" noPadding>
|
<ExistingTableDataImport
|
||||||
<Label grey extraSmall>CSV or JSON file to import</Label>
|
{tableId}
|
||||||
<TableDataImport
|
{tableType}
|
||||||
{tableId}
|
bind:rows
|
||||||
{tableType}
|
bind:allValid
|
||||||
bind:rows
|
bind:displayColumn
|
||||||
bind:allValid
|
bind:identifierFields
|
||||||
bind:displayColumn
|
/>
|
||||||
bind:identifierFields
|
|
||||||
/>
|
|
||||||
</Layout>
|
|
||||||
<div>
|
<div>
|
||||||
<Button cta disabled={loading || !allValid} on:click={importData}>
|
<Button cta disabled={loading || !allValid} on:click={importData}>
|
||||||
Import
|
Import
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
BBReferenceFieldSubType,
|
BBReferenceFieldSubType,
|
||||||
SourceName,
|
SourceName,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import { Select, Toggle, Multiselect } from "@budibase/bbui"
|
import { Select, Toggle, Multiselect, Label, Layout } from "@budibase/bbui"
|
||||||
import { DB_TYPE_INTERNAL } from "constants/backend"
|
import { DB_TYPE_INTERNAL } from "constants/backend"
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
import { parseFile } from "./utils"
|
import { parseFile } from "./utils"
|
||||||
|
@ -140,84 +140,91 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="dropzone">
|
<Layout gap="S" noPadding>
|
||||||
<input
|
<Layout noPadding gap="XS">
|
||||||
disabled={!schema || loading}
|
<Label grey extraSmall>CSV or JSON file to import</Label>
|
||||||
id="file-upload"
|
<div class="dropzone">
|
||||||
accept="text/csv,application/json"
|
<input
|
||||||
type="file"
|
disabled={!schema || loading}
|
||||||
on:change={handleFile}
|
id="file-upload"
|
||||||
/>
|
accept="text/csv,application/json"
|
||||||
<label for="file-upload" class:uploaded={rows.length > 0}>
|
type="file"
|
||||||
{#if loading}
|
on:change={handleFile}
|
||||||
loading...
|
|
||||||
{:else if error}
|
|
||||||
error: {error}
|
|
||||||
{:else if fileName}
|
|
||||||
{fileName}
|
|
||||||
{:else}
|
|
||||||
Upload
|
|
||||||
{/if}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
{#if fileName && Object.keys(validation).length === 0}
|
|
||||||
<p>No valid fields, try another file</p>
|
|
||||||
{:else if rows.length > 0 && !error}
|
|
||||||
<div class="schema-fields">
|
|
||||||
{#each Object.keys(validation) as name}
|
|
||||||
<div class="field">
|
|
||||||
<span>{name}</span>
|
|
||||||
<Select
|
|
||||||
value={`${schema[name]?.type}${schema[name]?.subtype || ""}`}
|
|
||||||
options={typeOptions}
|
|
||||||
placeholder={null}
|
|
||||||
getOptionLabel={option => option.label}
|
|
||||||
getOptionValue={option => option.value}
|
|
||||||
disabled
|
|
||||||
/>
|
|
||||||
<span
|
|
||||||
class={loading || validation[name]
|
|
||||||
? "fieldStatusSuccess"
|
|
||||||
: "fieldStatusFailure"}
|
|
||||||
>
|
|
||||||
{validation[name] ? "Success" : "Failure"}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
<br />
|
|
||||||
<!-- SQL Server doesn't yet support overwriting rows by existing keys -->
|
|
||||||
{#if datasource?.source !== SourceName.SQL_SERVER}
|
|
||||||
<Toggle
|
|
||||||
bind:value={updateExistingRows}
|
|
||||||
on:change={() => (identifierFields = [])}
|
|
||||||
thin
|
|
||||||
text="Update existing rows"
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
{#if updateExistingRows}
|
|
||||||
{#if tableType === DB_TYPE_INTERNAL}
|
|
||||||
<Multiselect
|
|
||||||
label="Identifier field(s)"
|
|
||||||
options={Object.keys(validation)}
|
|
||||||
bind:value={identifierFields}
|
|
||||||
/>
|
/>
|
||||||
{:else}
|
<label for="file-upload" class:uploaded={rows.length > 0}>
|
||||||
<p>Rows will be updated based on the table's primary key.</p>
|
{#if loading}
|
||||||
|
loading...
|
||||||
|
{:else if error}
|
||||||
|
error: {error}
|
||||||
|
{:else if fileName}
|
||||||
|
{fileName}
|
||||||
|
{:else}
|
||||||
|
Upload
|
||||||
|
{/if}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
|
||||||
|
{#if fileName && Object.keys(validation).length === 0}
|
||||||
|
<div>No valid fields - please try another file.</div>
|
||||||
|
{:else if fileName && rows.length > 0 && !error}
|
||||||
|
<div>
|
||||||
|
{#each Object.keys(validation) as name}
|
||||||
|
<div class="field">
|
||||||
|
<span>{name}</span>
|
||||||
|
<Select
|
||||||
|
value={`${schema[name]?.type}${schema[name]?.subtype || ""}`}
|
||||||
|
options={typeOptions}
|
||||||
|
placeholder={null}
|
||||||
|
getOptionLabel={option => option.label}
|
||||||
|
getOptionValue={option => option.value}
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
class={loading || validation[name]
|
||||||
|
? "fieldStatusSuccess"
|
||||||
|
: "fieldStatusFailure"}
|
||||||
|
>
|
||||||
|
{validation[name] ? "Success" : "Failure"}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
<!-- SQL Server doesn't yet support overwriting rows by existing keys -->
|
||||||
|
{#if datasource?.source !== SourceName.SQL_SERVER}
|
||||||
|
<Toggle
|
||||||
|
bind:value={updateExistingRows}
|
||||||
|
on:change={() => (identifierFields = [])}
|
||||||
|
thin
|
||||||
|
text="Update existing rows"
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
{#if updateExistingRows}
|
||||||
|
{#if tableType === DB_TYPE_INTERNAL}
|
||||||
|
<Multiselect
|
||||||
|
label="Identifier field(s)"
|
||||||
|
options={Object.keys(validation)}
|
||||||
|
bind:value={identifierFields}
|
||||||
|
/>
|
||||||
|
{:else}
|
||||||
|
<div>Rows will be updated based on the table's primary key.</div>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
{#if invalidColumns.length > 0}
|
||||||
|
<Layout noPadding gap="XS">
|
||||||
|
<div>
|
||||||
|
The following columns are present in the data you wish to import, but
|
||||||
|
do not match the schema of this table and will be ignored:
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{#each invalidColumns as column}
|
||||||
|
- {column}<br />
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
{#if invalidColumns.length > 0}
|
</Layout>
|
||||||
<p class="spectrum-FieldLabel spectrum-FieldLabel--sizeM">
|
|
||||||
The following columns are present in the data you wish to import, but do
|
|
||||||
not match the schema of this table and will be ignored.
|
|
||||||
</p>
|
|
||||||
<ul class="ignoredList">
|
|
||||||
{#each invalidColumns as column}
|
|
||||||
<li>{column}</li>
|
|
||||||
{/each}
|
|
||||||
</ul>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.dropzone {
|
.dropzone {
|
||||||
|
@ -228,11 +235,9 @@
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
label {
|
label {
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
@ -240,7 +245,6 @@
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border-radius: var(--border-radius-s);
|
border-radius: var(--border-radius-s);
|
||||||
color: var(--ink);
|
|
||||||
padding: var(--spacing-m) var(--spacing-l);
|
padding: var(--spacing-m) var(--spacing-l);
|
||||||
transition: all 0.2s ease 0s;
|
transition: all 0.2s ease 0s;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
@ -254,20 +258,14 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: var(--grey-2);
|
background-color: var(--spectrum-global-color-gray-300);
|
||||||
font-size: var(--font-size-xs);
|
font-size: var(--font-size-s);
|
||||||
line-height: normal;
|
line-height: normal;
|
||||||
border: var(--border-transparent);
|
border: var(--border-transparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.uploaded {
|
.uploaded {
|
||||||
color: var(--blue);
|
color: var(--spectrum-global-color-blue-600);
|
||||||
}
|
}
|
||||||
|
|
||||||
.schema-fields {
|
|
||||||
margin-top: var(--spacing-xl);
|
|
||||||
}
|
|
||||||
|
|
||||||
.field {
|
.field {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 2fr 2fr 1fr auto;
|
grid-template-columns: 2fr 2fr 1fr auto;
|
||||||
|
@ -276,23 +274,14 @@
|
||||||
grid-gap: var(--spacing-m);
|
grid-gap: var(--spacing-m);
|
||||||
font-size: var(--spectrum-global-dimension-font-size-75);
|
font-size: var(--spectrum-global-dimension-font-size-75);
|
||||||
}
|
}
|
||||||
|
|
||||||
.fieldStatusSuccess {
|
.fieldStatusSuccess {
|
||||||
color: var(--green);
|
color: var(--green);
|
||||||
justify-self: center;
|
justify-self: center;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fieldStatusFailure {
|
.fieldStatusFailure {
|
||||||
color: var(--red);
|
color: var(--red);
|
||||||
justify-self: center;
|
justify-self: center;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ignoredList {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
list-style: none;
|
|
||||||
font-size: var(--spectrum-global-dimension-font-size-75);
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { Select, Icon, Layout } from "@budibase/bbui"
|
import { Select, Icon, Layout, Label } from "@budibase/bbui"
|
||||||
import { FIELDS } from "constants/backend"
|
import { FIELDS } from "constants/backend"
|
||||||
import { utils } from "@budibase/shared-core"
|
import { utils } from "@budibase/shared-core"
|
||||||
import { canBeDisplayColumn } from "@budibase/frontend-core"
|
import { canBeDisplayColumn } from "@budibase/frontend-core"
|
||||||
|
@ -185,25 +185,30 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Layout noPadding gap="S">
|
<Layout noPadding gap="S">
|
||||||
<div class="dropzone">
|
<Layout gap="XS" noPadding>
|
||||||
<input
|
<Label grey extraSmall>
|
||||||
bind:this={fileInput}
|
Create a Table from a CSV or JSON file (Optional)
|
||||||
disabled={loading}
|
</Label>
|
||||||
id="file-upload"
|
<div class="dropzone">
|
||||||
accept="text/csv,application/json"
|
<input
|
||||||
type="file"
|
bind:this={fileInput}
|
||||||
on:change={handleFile}
|
disabled={loading}
|
||||||
/>
|
id="file-upload"
|
||||||
<label for="file-upload" class:uploaded={rawRows.length > 0}>
|
accept="text/csv,application/json"
|
||||||
{#if error}
|
type="file"
|
||||||
Error: {error}
|
on:change={handleFile}
|
||||||
{:else if fileName}
|
/>
|
||||||
{fileName}
|
<label for="file-upload" class:uploaded={rawRows.length > 0}>
|
||||||
{:else}
|
{#if error}
|
||||||
Upload
|
Error: {error}
|
||||||
{/if}
|
{:else if fileName}
|
||||||
</label>
|
{fileName}
|
||||||
</div>
|
{:else}
|
||||||
|
Upload
|
||||||
|
{/if}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
|
||||||
{#if rawRows.length > 0 && !error}
|
{#if rawRows.length > 0 && !error}
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -1,13 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { goto, url } from "@roxi/routify"
|
import { goto, url } from "@roxi/routify"
|
||||||
import { tables, datasources } from "stores/builder"
|
import { tables, datasources } from "stores/builder"
|
||||||
import {
|
import { notifications, Input, ModalContent } from "@budibase/bbui"
|
||||||
notifications,
|
|
||||||
Input,
|
|
||||||
Label,
|
|
||||||
ModalContent,
|
|
||||||
Layout,
|
|
||||||
} from "@budibase/bbui"
|
|
||||||
import TableDataImport from "../TableDataImport.svelte"
|
import TableDataImport from "../TableDataImport.svelte"
|
||||||
import {
|
import {
|
||||||
BUDIBASE_INTERNAL_DB_ID,
|
BUDIBASE_INTERNAL_DB_ID,
|
||||||
|
@ -101,18 +95,11 @@
|
||||||
bind:value={name}
|
bind:value={name}
|
||||||
{error}
|
{error}
|
||||||
/>
|
/>
|
||||||
<div>
|
<TableDataImport
|
||||||
<Layout gap="XS" noPadding>
|
{promptUpload}
|
||||||
<Label grey extraSmall
|
bind:rows
|
||||||
>Create a Table from a CSV or JSON file (Optional)</Label
|
bind:schema
|
||||||
>
|
bind:allValid
|
||||||
<TableDataImport
|
bind:displayColumn
|
||||||
{promptUpload}
|
/>
|
||||||
bind:rows
|
|
||||||
bind:schema
|
|
||||||
bind:allValid
|
|
||||||
bind:displayColumn
|
|
||||||
/>
|
|
||||||
</Layout>
|
|
||||||
</div>
|
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|
Loading…
Reference in New Issue