Add new modal to backend and fix backend file structure
This commit is contained in:
parent
d4ebd3bb63
commit
42a7a21050
|
@ -1,8 +1,8 @@
|
||||||
<script>
|
<script>
|
||||||
import { backendUiStore } from "builderStore"
|
import { backendUiStore } from "builderStore"
|
||||||
import RowPopover from "./popovers/Row.svelte"
|
import CreateRowButton from "./buttons/CreateRowButton.svelte"
|
||||||
import ColumnPopover from "./popovers/Column.svelte"
|
import CreateColumnButton from "./buttons/CreateColumnButton.svelte"
|
||||||
import ViewPopover from "./popovers/View.svelte"
|
import CreateViewButton from "./buttons/CreateViewButton.svelte"
|
||||||
import * as api from "./api"
|
import * as api from "./api"
|
||||||
import Table from "./Table.svelte"
|
import Table from "./Table.svelte"
|
||||||
|
|
||||||
|
@ -22,9 +22,9 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Table {title} {schema} {data} allowEditing={true}>
|
<Table {title} {schema} {data} allowEditing={true}>
|
||||||
<ColumnPopover />
|
<CreateColumnButton />
|
||||||
{#if Object.keys(schema).length > 0}
|
{#if Object.keys(schema).length > 0}
|
||||||
<RowPopover />
|
<CreateRowButton />
|
||||||
<ViewPopover />
|
<CreateViewButton />
|
||||||
{/if}
|
{/if}
|
||||||
</Table>
|
</Table>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { Input, Select, Label, DatePicker, Toggle } from "@budibase/bbui"
|
import { Input, Select, Label, DatePicker, Toggle } from "@budibase/bbui"
|
||||||
import Dropzone from "components/common/Dropzone.svelte"
|
import Dropzone from "components/common/Dropzone.svelte"
|
||||||
import { capitalise } from "../../../../helpers"
|
import { capitalise } from "../../../helpers"
|
||||||
|
|
||||||
export let meta
|
export let meta
|
||||||
export let value = meta.type === "boolean" ? false : ""
|
export let value = meta.type === "boolean" ? false : ""
|
|
@ -9,13 +9,13 @@
|
||||||
import ActionButton from "components/common/ActionButton.svelte"
|
import ActionButton from "components/common/ActionButton.svelte"
|
||||||
import AttachmentList from "./AttachmentList.svelte"
|
import AttachmentList from "./AttachmentList.svelte"
|
||||||
import TablePagination from "./TablePagination.svelte"
|
import TablePagination from "./TablePagination.svelte"
|
||||||
import CreateEditRecordModal from "./popovers/CreateEditRecord.svelte"
|
import CreateEditRecordModal from "./modals/CreateEditRecordModal.svelte"
|
||||||
import RowPopover from "./popovers/Row.svelte"
|
import RowPopover from "./buttons/CreateRowButton.svelte"
|
||||||
import ColumnPopover from "./popovers/Column.svelte"
|
import ColumnPopover from "./buttons/CreateColumnButton.svelte"
|
||||||
import ViewPopover from "./popovers/View.svelte"
|
import ViewPopover from "./buttons/CreateViewButton.svelte"
|
||||||
import ColumnHeaderPopover from "./popovers/ColumnHeader.svelte"
|
import ColumnHeaderPopover from "./popovers/ColumnPopover.svelte"
|
||||||
import EditRowPopover from "./popovers/EditRow.svelte"
|
import EditRowPopover from "./popovers/RowPopover.svelte"
|
||||||
import CalculationPopover from "./popovers/Calculate.svelte"
|
import CalculationPopover from "./buttons/CalculateButton.svelte"
|
||||||
|
|
||||||
const ITEMS_PER_PAGE = 10
|
const ITEMS_PER_PAGE = 10
|
||||||
|
|
||||||
|
@ -32,9 +32,9 @@
|
||||||
$: paginatedData =
|
$: paginatedData =
|
||||||
sorted && sorted.length
|
sorted && sorted.length
|
||||||
? sorted.slice(
|
? sorted.slice(
|
||||||
currentPage * ITEMS_PER_PAGE,
|
currentPage * ITEMS_PER_PAGE,
|
||||||
currentPage * ITEMS_PER_PAGE + ITEMS_PER_PAGE,
|
currentPage * ITEMS_PER_PAGE + ITEMS_PER_PAGE
|
||||||
)
|
)
|
||||||
: []
|
: []
|
||||||
$: modelId = data?.length ? data[0].modelId : null
|
$: modelId = data?.length ? data[0].modelId : null
|
||||||
|
|
||||||
|
@ -42,7 +42,9 @@
|
||||||
if (!record?.[fieldName]?.length) {
|
if (!record?.[fieldName]?.length) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
$goto(`/${$params.application}/backend/model/${modelId}/relationship/${record._id}/${fieldName}`)
|
$goto(
|
||||||
|
`/${$params.application}/backend/model/${modelId}/relationship/${record._id}/${fieldName}`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -50,68 +52,68 @@
|
||||||
<div class="table-controls">
|
<div class="table-controls">
|
||||||
<h2 class="title">{title}</h2>
|
<h2 class="title">{title}</h2>
|
||||||
<div class="popovers">
|
<div class="popovers">
|
||||||
<slot/>
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<table class="bb-table">
|
<table class="bb-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
|
||||||
{#if allowEditing}
|
|
||||||
<th class="edit-header">
|
|
||||||
<div>Edit</div>
|
|
||||||
</th>
|
|
||||||
{/if}
|
|
||||||
{#each columns as header}
|
|
||||||
<th>
|
|
||||||
{#if allowEditing}
|
|
||||||
<ColumnHeaderPopover field={schema[header]}/>
|
|
||||||
{:else}
|
|
||||||
<div class="header">{header}</div>
|
|
||||||
{/if}
|
|
||||||
</th>
|
|
||||||
{/each}
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{#if paginatedData.length === 0}
|
|
||||||
{#if allowEditing}
|
|
||||||
<td class="no-border">No data.</td>
|
|
||||||
{/if}
|
|
||||||
{#each columns as header, idx}
|
|
||||||
<td class="no-border">
|
|
||||||
{#if idx === 0}No data.{/if}
|
|
||||||
</td>
|
|
||||||
{/each}
|
|
||||||
{/if}
|
|
||||||
{#each paginatedData as row}
|
|
||||||
<tr>
|
<tr>
|
||||||
{#if allowEditing}
|
{#if allowEditing}
|
||||||
<td>
|
<th class="edit-header">
|
||||||
<EditRowPopover {row}/>
|
<div>Edit</div>
|
||||||
</td>
|
</th>
|
||||||
{/if}
|
{/if}
|
||||||
{#each columns as header}
|
{#each columns as header}
|
||||||
<td>
|
<th>
|
||||||
{#if schema[header].type === 'link'}
|
{#if allowEditing}
|
||||||
<div
|
<ColumnHeaderPopover field={schema[header]} />
|
||||||
class:link={row[header] && row[header].length}
|
{:else}
|
||||||
on:click={() => selectRelationship(row, header)}>
|
<div class="header">{header}</div>
|
||||||
{row[header] ? row[header].length : 0} linked row(s)
|
{/if}
|
||||||
</div>
|
</th>
|
||||||
{:else if schema[header].type === 'attachment'}
|
|
||||||
<AttachmentList files={row[header] || []}/>
|
|
||||||
{:else}{getOr('', header, row)}{/if}
|
|
||||||
</td>
|
|
||||||
{/each}
|
{/each}
|
||||||
</tr>
|
</tr>
|
||||||
{/each}
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{#if paginatedData.length === 0}
|
||||||
|
{#if allowEditing}
|
||||||
|
<td class="no-border">No data.</td>
|
||||||
|
{/if}
|
||||||
|
{#each columns as header, idx}
|
||||||
|
<td class="no-border">
|
||||||
|
{#if idx === 0}No data.{/if}
|
||||||
|
</td>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
{#each paginatedData as row}
|
||||||
|
<tr>
|
||||||
|
{#if allowEditing}
|
||||||
|
<td>
|
||||||
|
<EditRowPopover {row} />
|
||||||
|
</td>
|
||||||
|
{/if}
|
||||||
|
{#each columns as header}
|
||||||
|
<td>
|
||||||
|
{#if schema[header].type === 'link'}
|
||||||
|
<div
|
||||||
|
class:link={row[header] && row[header].length}
|
||||||
|
on:click={() => selectRelationship(row, header)}>
|
||||||
|
{row[header] ? row[header].length : 0} linked row(s)
|
||||||
|
</div>
|
||||||
|
{:else if schema[header].type === 'attachment'}
|
||||||
|
<AttachmentList files={row[header] || []} />
|
||||||
|
{:else}{getOr('', header, row)}{/if}
|
||||||
|
</td>
|
||||||
|
{/each}
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<TablePagination
|
<TablePagination
|
||||||
{data}
|
{data}
|
||||||
bind:currentPage
|
bind:currentPage
|
||||||
pageItemCount={paginatedData.length}
|
pageItemCount={paginatedData.length}
|
||||||
{ITEMS_PER_PAGE}/>
|
{ITEMS_PER_PAGE} />
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<script>
|
<script>
|
||||||
import api from "builderStore/api"
|
import api from "builderStore/api"
|
||||||
import Table from "./Table.svelte"
|
import Table from "./Table.svelte"
|
||||||
import CalculationPopover from "./popovers/Calculate.svelte"
|
import CalculateButton from "./buttons/CalculateButton.svelte"
|
||||||
import GroupByPopover from "./popovers/GroupBy.svelte"
|
import GroupByButton from "./buttons/GroupByButton.svelte"
|
||||||
import FilterPopover from "./popovers/Filter.svelte"
|
import FilterButton from "./buttons/FilterButton.svelte"
|
||||||
|
|
||||||
export let view = {}
|
export let view = {}
|
||||||
|
|
||||||
|
@ -34,9 +34,9 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Table title={decodeURI(name)} schema={view.schema} {data}>
|
<Table title={decodeURI(name)} schema={view.schema} {data}>
|
||||||
<FilterPopover {view} />
|
<FilterButton {view} />
|
||||||
<CalculationPopover {view} />
|
<CalculateButton {view} />
|
||||||
{#if view.calculation}
|
{#if view.calculation}
|
||||||
<GroupByPopover {view} />
|
<GroupByButton {view} />
|
||||||
{/if}
|
{/if}
|
||||||
</Table>
|
</Table>
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
<script>
|
||||||
|
import { Popover, TextButton, Icon } from "@budibase/bbui"
|
||||||
|
import CalculatePopover from "../popovers/CalculatePopover.svelte"
|
||||||
|
|
||||||
|
export let view = {}
|
||||||
|
|
||||||
|
let anchor
|
||||||
|
let dropdown
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={anchor}>
|
||||||
|
<TextButton text small on:click={dropdown.show} active={!!view.field}>
|
||||||
|
<Icon name="calculate" />
|
||||||
|
Calculate
|
||||||
|
</TextButton>
|
||||||
|
</div>
|
||||||
|
<Popover bind:this={dropdown} {anchor} align="left">
|
||||||
|
<CalculatePopover {view} onClosed={dropdown.hide} />
|
||||||
|
</Popover>
|
|
@ -1,14 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { backendUiStore } from "builderStore"
|
import { DropdownMenu, TextButton as Button, Icon } from "@budibase/bbui"
|
||||||
import {
|
import CreateEditColumnPopover from "../popovers/CreateEditColumnPopover.svelte"
|
||||||
DropdownMenu,
|
|
||||||
TextButton as Button,
|
|
||||||
Icon,
|
|
||||||
Input,
|
|
||||||
Select,
|
|
||||||
} from "@budibase/bbui"
|
|
||||||
import { FIELDS } from "constants/backend"
|
|
||||||
import CreateEditColumn from "./CreateEditColumn.svelte"
|
|
||||||
|
|
||||||
let anchor
|
let anchor
|
||||||
let dropdown
|
let dropdown
|
||||||
|
@ -23,7 +15,7 @@
|
||||||
</div>
|
</div>
|
||||||
<DropdownMenu bind:this={dropdown} {anchor} align="left">
|
<DropdownMenu bind:this={dropdown} {anchor} align="left">
|
||||||
<h5>Create Column</h5>
|
<h5>Create Column</h5>
|
||||||
<CreateEditColumn onClosed={dropdown.hide} />
|
<CreateEditColumnPopover onClosed={dropdown.hide} />
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
|
||||||
<style>
|
<style>
|
|
@ -0,0 +1,17 @@
|
||||||
|
<script>
|
||||||
|
import { TextButton as Button, Icon } from "@budibase/bbui"
|
||||||
|
import CreateEditRecordModal from "../modals/CreateEditRecordModal.svelte"
|
||||||
|
import { Modal } from "components/common/Modal"
|
||||||
|
|
||||||
|
let modal
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Button text small on:click={modal.show}>
|
||||||
|
<Icon name="addrow" />
|
||||||
|
Create New Row
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<Modal bind:this={modal}>
|
||||||
|
<CreateEditRecordModal />
|
||||||
|
</Modal>
|
|
@ -0,0 +1,17 @@
|
||||||
|
<script>
|
||||||
|
import { Popover, TextButton, Icon } from "@budibase/bbui"
|
||||||
|
import CreateViewPopover from "../popovers/CreateViewPopover.svelte"
|
||||||
|
|
||||||
|
let anchor
|
||||||
|
let dropdown
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={anchor}>
|
||||||
|
<TextButton text small on:click={dropdown.show}>
|
||||||
|
<Icon name="view" />
|
||||||
|
Create New View
|
||||||
|
</TextButton>
|
||||||
|
</div>
|
||||||
|
<Popover bind:this={dropdown} {anchor} align="left">
|
||||||
|
<CreateViewPopover onClosed={dropdown.hide} />
|
||||||
|
</Popover>
|
|
@ -0,0 +1,23 @@
|
||||||
|
<script>
|
||||||
|
import { Popover, TextButton, Icon } from "@budibase/bbui"
|
||||||
|
import FilterPopover from "../popovers/FilterPopover.svelte"
|
||||||
|
|
||||||
|
export let view = {}
|
||||||
|
|
||||||
|
let anchor
|
||||||
|
let dropdown
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={anchor}>
|
||||||
|
<TextButton
|
||||||
|
text
|
||||||
|
small
|
||||||
|
on:click={dropdown.show}
|
||||||
|
active={view.filters && view.filters.length}>
|
||||||
|
<Icon name="filter" />
|
||||||
|
Filter
|
||||||
|
</TextButton>
|
||||||
|
</div>
|
||||||
|
<Popover bind:this={dropdown} {anchor} align="left">
|
||||||
|
<FilterPopover {view} onClosed={dropdown.hide} />
|
||||||
|
</Popover>
|
|
@ -0,0 +1,19 @@
|
||||||
|
<script>
|
||||||
|
import { Popover, TextButton, Icon } from "@budibase/bbui"
|
||||||
|
import GroupByPopover from "../popovers/GroupByPopover.svelte"
|
||||||
|
|
||||||
|
export let view = {}
|
||||||
|
|
||||||
|
let anchor
|
||||||
|
let dropdown
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={anchor}>
|
||||||
|
<TextButton text small active={!!view.groupBy} on:click={dropdown.show}>
|
||||||
|
<Icon name="group" />
|
||||||
|
Group By
|
||||||
|
</TextButton>
|
||||||
|
</div>
|
||||||
|
<Popover bind:this={dropdown} {anchor} align="left">
|
||||||
|
<GroupByPopover {view} onClosed={dropdown.hide} />
|
||||||
|
</Popover>
|
|
@ -1 +0,0 @@
|
||||||
export { default } from "./ModelDataTable.svelte"
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
<script>
|
||||||
|
import { backendUiStore } from "builderStore"
|
||||||
|
import { notifier } from "builderStore/store/notifications"
|
||||||
|
import LinkedRecordSelector from "components/common/LinkedRecordSelector.svelte"
|
||||||
|
import RecordFieldControl from "../RecordFieldControl.svelte"
|
||||||
|
import * as api from "../api"
|
||||||
|
import { ModalTitle, ModalFooter } from "components/common/Modal"
|
||||||
|
import ErrorsBox from "components/common/ErrorsBox.svelte"
|
||||||
|
|
||||||
|
export let record = {}
|
||||||
|
export let visible = false
|
||||||
|
|
||||||
|
let modal
|
||||||
|
let errors = []
|
||||||
|
|
||||||
|
$: creating = record?._id == null
|
||||||
|
$: model = record.modelId
|
||||||
|
? $backendUiStore.models.find(model => model._id === record?.modelId)
|
||||||
|
: $backendUiStore.selectedModel
|
||||||
|
$: modelSchema = Object.entries(model?.schema ?? {})
|
||||||
|
|
||||||
|
async function saveRecord() {
|
||||||
|
const recordResponse = await api.saveRecord(
|
||||||
|
{ ...record, modelId: model._id },
|
||||||
|
model._id
|
||||||
|
)
|
||||||
|
if (recordResponse.errors) {
|
||||||
|
errors = Object.keys(recordResponse.errors)
|
||||||
|
.map(k => ({ dataPath: k, message: recordResponse.errors[k] }))
|
||||||
|
.flat()
|
||||||
|
// Prevent modal closing if there were errors
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
notifier.success("Record saved successfully.")
|
||||||
|
backendUiStore.actions.records.save(recordResponse)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ModalTitle>{creating ? 'Create Row' : 'Edit Row'}</ModalTitle>
|
||||||
|
<ErrorsBox {errors} />
|
||||||
|
{#each modelSchema as [key, meta]}
|
||||||
|
<div>
|
||||||
|
{#if meta.type === 'link'}
|
||||||
|
<LinkedRecordSelector bind:linkedRecords={record[key]} schema={meta} />
|
||||||
|
{:else}
|
||||||
|
<RecordFieldControl {meta} bind:value={record[key]} />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
<ModalFooter confirmText={creating ? 'Add' : 'Save'} onConfirm={saveRecord} />
|
|
@ -1,102 +0,0 @@
|
||||||
<script>
|
|
||||||
import {
|
|
||||||
Popover,
|
|
||||||
TextButton,
|
|
||||||
Button,
|
|
||||||
Icon,
|
|
||||||
Input,
|
|
||||||
Select,
|
|
||||||
} from "@budibase/bbui"
|
|
||||||
import { backendUiStore } from "builderStore"
|
|
||||||
import { notifier } from "builderStore/store/notifications"
|
|
||||||
import CreateEditRecord from "./CreateEditRecord.svelte"
|
|
||||||
|
|
||||||
const CALCULATIONS = [
|
|
||||||
{
|
|
||||||
name: "Statistics",
|
|
||||||
key: "stats",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
export let view = {}
|
|
||||||
|
|
||||||
let anchor
|
|
||||||
let dropdown
|
|
||||||
|
|
||||||
$: viewModel = $backendUiStore.models.find(
|
|
||||||
({ _id }) => _id === $backendUiStore.selectedView.modelId
|
|
||||||
)
|
|
||||||
$: fields =
|
|
||||||
viewModel &&
|
|
||||||
Object.keys(viewModel.schema).filter(
|
|
||||||
field => viewModel.schema[field].type === "number"
|
|
||||||
)
|
|
||||||
|
|
||||||
function saveView() {
|
|
||||||
backendUiStore.actions.views.save(view)
|
|
||||||
notifier.success(`View ${view.name} saved.`)
|
|
||||||
dropdown.hide()
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div bind:this={anchor}>
|
|
||||||
<TextButton text small on:click={dropdown.show} active={!!view.field}>
|
|
||||||
<Icon name="calculate" />
|
|
||||||
Calculate
|
|
||||||
</TextButton>
|
|
||||||
</div>
|
|
||||||
<Popover bind:this={dropdown} {anchor} align="left">
|
|
||||||
<div class="actions">
|
|
||||||
<h5>Calculate</h5>
|
|
||||||
<div class="input-group-row">
|
|
||||||
<p>The</p>
|
|
||||||
<Select secondary thin bind:value={view.calculation}>
|
|
||||||
<option value="">Choose an option</option>
|
|
||||||
{#each CALCULATIONS as calculation}
|
|
||||||
<option value={calculation.key}>{calculation.name}</option>
|
|
||||||
{/each}
|
|
||||||
</Select>
|
|
||||||
<p>of</p>
|
|
||||||
<Select secondary thin bind:value={view.field}>
|
|
||||||
<option value="">Choose an option</option>
|
|
||||||
{#each fields as field}
|
|
||||||
<option value={field}>{field}</option>
|
|
||||||
{/each}
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
<div class="footer">
|
|
||||||
<Button secondary on:click={dropdown.hide}>Cancel</Button>
|
|
||||||
<Button primary on:click={saveView}>Save</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Popover>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.actions {
|
|
||||||
display: grid;
|
|
||||||
grid-gap: var(--spacing-xl);
|
|
||||||
}
|
|
||||||
|
|
||||||
h5 {
|
|
||||||
margin: 0;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
gap: var(--spacing-m);
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group-row {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 30px 1fr 20px 1fr;
|
|
||||||
gap: var(--spacing-s);
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0;
|
|
||||||
font-size: var(--font-size-xs);
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
<script>
|
||||||
|
import { Button, Input, Select } from "@budibase/bbui"
|
||||||
|
import { backendUiStore } from "builderStore"
|
||||||
|
import { notifier } from "builderStore/store/notifications"
|
||||||
|
|
||||||
|
const CALCULATIONS = [
|
||||||
|
{
|
||||||
|
name: "Statistics",
|
||||||
|
key: "stats",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export let view = {}
|
||||||
|
export let onClosed
|
||||||
|
|
||||||
|
$: viewModel = $backendUiStore.models.find(
|
||||||
|
({ _id }) => _id === $backendUiStore.selectedView.modelId
|
||||||
|
)
|
||||||
|
$: fields =
|
||||||
|
viewModel &&
|
||||||
|
Object.keys(viewModel.schema).filter(
|
||||||
|
field => viewModel.schema[field].type === "number"
|
||||||
|
)
|
||||||
|
|
||||||
|
function saveView() {
|
||||||
|
backendUiStore.actions.views.save(view)
|
||||||
|
notifier.success(`View ${view.name} saved.`)
|
||||||
|
onClosed()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="actions">
|
||||||
|
<h5>Calculate</h5>
|
||||||
|
<div class="input-group-row">
|
||||||
|
<p>The</p>
|
||||||
|
<Select secondary thin bind:value={view.calculation}>
|
||||||
|
<option value="">Choose an option</option>
|
||||||
|
{#each CALCULATIONS as calculation}
|
||||||
|
<option value={calculation.key}>{calculation.name}</option>
|
||||||
|
{/each}
|
||||||
|
</Select>
|
||||||
|
<p>of</p>
|
||||||
|
<Select secondary thin bind:value={view.field}>
|
||||||
|
<option value="">Choose an option</option>
|
||||||
|
{#each fields as field}
|
||||||
|
<option value={field}>{field}</option>
|
||||||
|
{/each}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
<div class="footer">
|
||||||
|
<Button secondary on:click={onClosed}>Cancel</Button>
|
||||||
|
<Button primary on:click={saveView}>Save</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.actions {
|
||||||
|
display: grid;
|
||||||
|
grid-gap: var(--spacing-xl);
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: var(--spacing-m);
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 30px 1fr 20px 1fr;
|
||||||
|
gap: var(--spacing-s);
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
font-size: var(--font-size-xs);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -2,7 +2,7 @@
|
||||||
import { backendUiStore } from "builderStore"
|
import { backendUiStore } from "builderStore"
|
||||||
import { DropdownMenu, Button, Icon, Input, Select } from "@budibase/bbui"
|
import { DropdownMenu, Button, Icon, Input, Select } from "@budibase/bbui"
|
||||||
import { FIELDS } from "constants/backend"
|
import { FIELDS } from "constants/backend"
|
||||||
import CreateEditColumnModal from "./CreateEditColumn.svelte"
|
import CreateEditColumnPopover from "./CreateEditColumnPopover.svelte"
|
||||||
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
||||||
import { notifier } from "../../../../builderStore/store/notifications"
|
import { notifier } from "../../../../builderStore/store/notifications"
|
||||||
|
|
||||||
|
@ -26,6 +26,11 @@
|
||||||
editing = false
|
editing = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showDelete() {
|
||||||
|
dropdown.hide()
|
||||||
|
confirmDeleteDialog.show()
|
||||||
|
}
|
||||||
|
|
||||||
function deleteColumn() {
|
function deleteColumn() {
|
||||||
if (field.name === $backendUiStore.selectedModel.primaryDisplay) {
|
if (field.name === $backendUiStore.selectedModel.primaryDisplay) {
|
||||||
notifier.danger("You cannot delete the primary display column")
|
notifier.danger("You cannot delete the primary display column")
|
||||||
|
@ -52,7 +57,7 @@
|
||||||
<DropdownMenu bind:this={dropdown} {anchor} align="left">
|
<DropdownMenu bind:this={dropdown} {anchor} align="left">
|
||||||
{#if editing}
|
{#if editing}
|
||||||
<h5>Edit Column</h5>
|
<h5>Edit Column</h5>
|
||||||
<CreateEditColumnModal onClosed={hideEditor} {field} />
|
<CreateEditColumnPopover onClosed={hideEditor} {field} />
|
||||||
{:else}
|
{:else}
|
||||||
<ul>
|
<ul>
|
||||||
{#if type !== 'link'}
|
{#if type !== 'link'}
|
||||||
|
@ -61,9 +66,7 @@
|
||||||
Edit
|
Edit
|
||||||
</li>
|
</li>
|
||||||
{/if}
|
{/if}
|
||||||
<li
|
<li data-cy="delete-column-header" on:click={showDelete}>
|
||||||
data-cy="delete-column-header"
|
|
||||||
on:click={() => confirmDeleteDialog.show()}>
|
|
||||||
<Icon name="delete" />
|
<Icon name="delete" />
|
||||||
Delete
|
Delete
|
||||||
</li>
|
</li>
|
|
@ -1,81 +0,0 @@
|
||||||
<script>
|
|
||||||
import { onMount, tick } from "svelte"
|
|
||||||
import { store, backendUiStore } from "builderStore"
|
|
||||||
import { notifier } from "builderStore/store/notifications"
|
|
||||||
import { compose, map, get, flatten } from "lodash/fp"
|
|
||||||
import { Input, TextArea, Button } from "@budibase/bbui"
|
|
||||||
import LinkedRecordSelector from "components/common/LinkedRecordSelector.svelte"
|
|
||||||
import RecordFieldControl from "./RecordFieldControl.svelte"
|
|
||||||
import * as api from "../api"
|
|
||||||
import ErrorsBox from "components/common/ErrorsBox.svelte"
|
|
||||||
|
|
||||||
export let record = {}
|
|
||||||
export let onClosed
|
|
||||||
|
|
||||||
let errors = []
|
|
||||||
$: model = record.modelId
|
|
||||||
? $backendUiStore.models.find(model => model._id === record?.modelId)
|
|
||||||
: $backendUiStore.selectedModel
|
|
||||||
$: modelSchema = Object.entries(model?.schema ?? {})
|
|
||||||
|
|
||||||
async function saveRecord() {
|
|
||||||
const recordResponse = await api.saveRecord(
|
|
||||||
{
|
|
||||||
...record,
|
|
||||||
modelId: model._id,
|
|
||||||
},
|
|
||||||
model._id
|
|
||||||
)
|
|
||||||
if (recordResponse.errors) {
|
|
||||||
errors = Object.keys(recordResponse.errors)
|
|
||||||
.map(k => ({ dataPath: k, message: recordResponse.errors[k] }))
|
|
||||||
.flat()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
onClosed()
|
|
||||||
notifier.success("Record saved successfully.")
|
|
||||||
backendUiStore.actions.records.save(recordResponse)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="actions">
|
|
||||||
<ErrorsBox {errors} />
|
|
||||||
<form on:submit|preventDefault>
|
|
||||||
{#each modelSchema as [key, meta]}
|
|
||||||
<div>
|
|
||||||
{#if meta.type === 'link'}
|
|
||||||
<LinkedRecordSelector
|
|
||||||
bind:linkedRecords={record[key]}
|
|
||||||
schema={meta} />
|
|
||||||
{:else}
|
|
||||||
<RecordFieldControl {meta} bind:value={record[key]} />
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</form>
|
|
||||||
<footer>
|
|
||||||
<Button secondary on:click={onClosed}>Cancel</Button>
|
|
||||||
<Button primary on:click={saveRecord}>Save</Button>
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.actions {
|
|
||||||
padding: var(--spacing-xl);
|
|
||||||
display: grid;
|
|
||||||
grid-gap: var(--spacing-xl);
|
|
||||||
min-width: 400px;
|
|
||||||
}
|
|
||||||
|
|
||||||
form {
|
|
||||||
display: grid;
|
|
||||||
grid-gap: var(--spacing-xl);
|
|
||||||
}
|
|
||||||
|
|
||||||
footer {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
gap: var(--spacing-m);
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,19 +1,10 @@
|
||||||
<script>
|
<script>
|
||||||
import {
|
import { Button, Input, Select } from "@budibase/bbui"
|
||||||
Popover,
|
|
||||||
TextButton,
|
|
||||||
Button,
|
|
||||||
Icon,
|
|
||||||
Input,
|
|
||||||
Select,
|
|
||||||
} from "@budibase/bbui"
|
|
||||||
import { goto } from "@sveltech/routify"
|
import { goto } from "@sveltech/routify"
|
||||||
import { backendUiStore } from "builderStore"
|
import { backendUiStore } from "builderStore"
|
||||||
import { notifier } from "builderStore/store/notifications"
|
import { notifier } from "builderStore/store/notifications"
|
||||||
import CreateEditRecord from "./CreateEditRecord.svelte"
|
|
||||||
|
|
||||||
let anchor
|
export let onClosed
|
||||||
let dropdown
|
|
||||||
|
|
||||||
let name
|
let name
|
||||||
let field
|
let field
|
||||||
|
@ -36,27 +27,19 @@
|
||||||
field,
|
field,
|
||||||
})
|
})
|
||||||
notifier.success(`View ${name} created`)
|
notifier.success(`View ${name} created`)
|
||||||
dropdown.hide()
|
onClosed()
|
||||||
$goto(`../../../view/${name}`)
|
$goto(`../../../view/${name}`)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div bind:this={anchor}>
|
<div class="actions">
|
||||||
<TextButton text small on:click={dropdown.show}>
|
<h5>Create View</h5>
|
||||||
<Icon name="view" />
|
<Input label="View Name" thin bind:value={name} />
|
||||||
Create New View
|
<div class="footer">
|
||||||
</TextButton>
|
<Button secondary on:click={onClosed}>Cancel</Button>
|
||||||
</div>
|
<Button primary on:click={saveView}>Save View</Button>
|
||||||
<Popover bind:this={dropdown} {anchor} align="left">
|
|
||||||
<div class="actions">
|
|
||||||
<h5>Create View</h5>
|
|
||||||
<Input label="View Name" thin bind:value={name} />
|
|
||||||
<div class="footer">
|
|
||||||
<Button secondary on:click={dropdown.hide}>Cancel</Button>
|
|
||||||
<Button primary on:click={saveView}>Save View</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Popover>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
h5 {
|
h5 {
|
|
@ -1,15 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import {
|
import { Button, Input, Select } from "@budibase/bbui"
|
||||||
Popover,
|
|
||||||
TextButton,
|
|
||||||
Button,
|
|
||||||
Icon,
|
|
||||||
Input,
|
|
||||||
Select,
|
|
||||||
} from "@budibase/bbui"
|
|
||||||
import { backendUiStore } from "builderStore"
|
import { backendUiStore } from "builderStore"
|
||||||
import { notifier } from "builderStore/store/notifications"
|
import { notifier } from "builderStore/store/notifications"
|
||||||
import CreateEditRecord from "./CreateEditRecord.svelte"
|
|
||||||
|
|
||||||
const CONDITIONS = [
|
const CONDITIONS = [
|
||||||
{
|
{
|
||||||
|
@ -50,9 +42,7 @@
|
||||||
]
|
]
|
||||||
|
|
||||||
export let view = {}
|
export let view = {}
|
||||||
|
export let onClosed
|
||||||
let anchor
|
|
||||||
let dropdown
|
|
||||||
|
|
||||||
$: viewModel = $backendUiStore.models.find(
|
$: viewModel = $backendUiStore.models.find(
|
||||||
({ _id }) => _id === $backendUiStore.selectedView.modelId
|
({ _id }) => _id === $backendUiStore.selectedView.modelId
|
||||||
|
@ -62,7 +52,7 @@
|
||||||
function saveView() {
|
function saveView() {
|
||||||
backendUiStore.actions.views.save(view)
|
backendUiStore.actions.views.save(view)
|
||||||
notifier.success(`View ${view.name} saved.`)
|
notifier.success(`View ${view.name} saved.`)
|
||||||
dropdown.hide()
|
onClosed()
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeFilter(idx) {
|
function removeFilter(idx) {
|
||||||
|
@ -84,67 +74,55 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div bind:this={anchor}>
|
<div class="actions">
|
||||||
<TextButton
|
<h5>Filter</h5>
|
||||||
text
|
{#if view.filters.length}
|
||||||
small
|
<div class="input-group-row">
|
||||||
on:click={dropdown.show}
|
{#each view.filters as filter, idx}
|
||||||
active={view.filters && view.filters.length}>
|
{#if idx === 0}
|
||||||
<Icon name="filter" />
|
<p>Where</p>
|
||||||
Filter
|
{:else}
|
||||||
</TextButton>
|
<Select secondary thin bind:value={filter.conjunction}>
|
||||||
</div>
|
|
||||||
<Popover bind:this={dropdown} {anchor} align="left">
|
|
||||||
<div class="actions">
|
|
||||||
<h5>Filter</h5>
|
|
||||||
{#if view.filters.length}
|
|
||||||
<div class="input-group-row">
|
|
||||||
{#each view.filters as filter, idx}
|
|
||||||
{#if idx === 0}
|
|
||||||
<p>Where</p>
|
|
||||||
{:else}
|
|
||||||
<Select secondary thin bind:value={filter.conjunction}>
|
|
||||||
<option value="">Choose an option</option>
|
|
||||||
{#each CONJUNCTIONS as conjunction}
|
|
||||||
<option value={conjunction.key}>{conjunction.name}</option>
|
|
||||||
{/each}
|
|
||||||
</Select>
|
|
||||||
{/if}
|
|
||||||
<Select secondary thin bind:value={filter.key}>
|
|
||||||
<option value="">Choose an option</option>
|
<option value="">Choose an option</option>
|
||||||
{#each fields as field}
|
{#each CONJUNCTIONS as conjunction}
|
||||||
<option value={field}>{field}</option>
|
<option value={conjunction.key}>{conjunction.name}</option>
|
||||||
{/each}
|
{/each}
|
||||||
</Select>
|
</Select>
|
||||||
<Select secondary thin bind:value={filter.condition}>
|
{/if}
|
||||||
|
<Select secondary thin bind:value={filter.key}>
|
||||||
|
<option value="">Choose an option</option>
|
||||||
|
{#each fields as field}
|
||||||
|
<option value={field}>{field}</option>
|
||||||
|
{/each}
|
||||||
|
</Select>
|
||||||
|
<Select secondary thin bind:value={filter.condition}>
|
||||||
|
<option value="">Choose an option</option>
|
||||||
|
{#each CONDITIONS as condition}
|
||||||
|
<option value={condition.key}>{condition.name}</option>
|
||||||
|
{/each}
|
||||||
|
</Select>
|
||||||
|
{#if filter.key && isMultipleChoice(filter.key)}
|
||||||
|
<Select secondary thin bind:value={filter.value}>
|
||||||
<option value="">Choose an option</option>
|
<option value="">Choose an option</option>
|
||||||
{#each CONDITIONS as condition}
|
{#each viewModel.schema[filter.key].constraints.inclusion as option}
|
||||||
<option value={condition.key}>{condition.name}</option>
|
<option value={option}>{option}</option>
|
||||||
{/each}
|
{/each}
|
||||||
</Select>
|
</Select>
|
||||||
{#if filter.key && isMultipleChoice(filter.key)}
|
{:else}
|
||||||
<Select secondary thin bind:value={filter.value}>
|
<Input thin placeholder="Value" bind:value={filter.value} />
|
||||||
<option value="">Choose an option</option>
|
{/if}
|
||||||
{#each viewModel.schema[filter.key].constraints.inclusion as option}
|
<i class="ri-close-circle-fill" on:click={() => removeFilter(idx)} />
|
||||||
<option value={option}>{option}</option>
|
{/each}
|
||||||
{/each}
|
</div>
|
||||||
</Select>
|
{/if}
|
||||||
{:else}
|
<div class="footer">
|
||||||
<Input thin placeholder="Value" bind:value={filter.value} />
|
<Button text on:click={addFilter}>Add Filter</Button>
|
||||||
{/if}
|
<div class="buttons">
|
||||||
<i class="ri-close-circle-fill" on:click={() => removeFilter(idx)} />
|
<Button secondary on:click={onClosed}>Cancel</Button>
|
||||||
{/each}
|
<Button primary on:click={saveView}>Save</Button>
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
<div class="footer">
|
|
||||||
<Button text on:click={addFilter}>Add Filter</Button>
|
|
||||||
<div class="buttons">
|
|
||||||
<Button secondary on:click={dropdown.hide}>Cancel</Button>
|
|
||||||
<Button primary on:click={saveView}>Save</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Popover>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.actions {
|
.actions {
|
|
@ -1,91 +0,0 @@
|
||||||
<script>
|
|
||||||
import {
|
|
||||||
Popover,
|
|
||||||
TextButton,
|
|
||||||
Button,
|
|
||||||
Icon,
|
|
||||||
Input,
|
|
||||||
Select,
|
|
||||||
} from "@budibase/bbui"
|
|
||||||
import { backendUiStore } from "builderStore"
|
|
||||||
import { notifier } from "builderStore/store/notifications"
|
|
||||||
import CreateEditRecord from "./CreateEditRecord.svelte"
|
|
||||||
|
|
||||||
const CALCULATIONS = [
|
|
||||||
{
|
|
||||||
name: "Statistics",
|
|
||||||
key: "stats",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
export let view = {}
|
|
||||||
|
|
||||||
let anchor
|
|
||||||
let dropdown
|
|
||||||
|
|
||||||
$: viewModel = $backendUiStore.models.find(
|
|
||||||
({ _id }) => _id === $backendUiStore.selectedView.modelId
|
|
||||||
)
|
|
||||||
$: fields = viewModel && Object.keys(viewModel.schema)
|
|
||||||
|
|
||||||
function saveView() {
|
|
||||||
backendUiStore.actions.views.save(view)
|
|
||||||
notifier.success(`View ${view.name} saved.`)
|
|
||||||
dropdown.hide()
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div bind:this={anchor}>
|
|
||||||
<TextButton text small active={!!view.groupBy} on:click={dropdown.show}>
|
|
||||||
<Icon name="group" />
|
|
||||||
Group
|
|
||||||
</TextButton>
|
|
||||||
</div>
|
|
||||||
<Popover bind:this={dropdown} {anchor} align="left">
|
|
||||||
<div class="actions">
|
|
||||||
<h5>Group</h5>
|
|
||||||
<div class="input-group-row">
|
|
||||||
<p>By</p>
|
|
||||||
<Select secondary thin bind:value={view.groupBy}>
|
|
||||||
<option value="">Choose an option</option>
|
|
||||||
{#each fields as field}
|
|
||||||
<option value={field}>{field}</option>
|
|
||||||
{/each}
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
<div class="footer">
|
|
||||||
<Button secondary on:click={dropdown.hide}>Cancel</Button>
|
|
||||||
<Button primary on:click={saveView}>Save</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Popover>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.actions {
|
|
||||||
display: grid;
|
|
||||||
grid-gap: var(--spacing-xl);
|
|
||||||
}
|
|
||||||
|
|
||||||
h5 {
|
|
||||||
margin: 0;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
gap: var(--spacing-m);
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group-row {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 20px 1fr;
|
|
||||||
gap: var(--spacing-s);
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0;
|
|
||||||
font-size: var(--font-size-xs);
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
<script>
|
||||||
|
import { Button, Input, Select } from "@budibase/bbui"
|
||||||
|
import { backendUiStore } from "builderStore"
|
||||||
|
import { notifier } from "builderStore/store/notifications"
|
||||||
|
|
||||||
|
export let view = {}
|
||||||
|
export let onClosed
|
||||||
|
|
||||||
|
$: viewModel = $backendUiStore.models.find(
|
||||||
|
({ _id }) => _id === $backendUiStore.selectedView.modelId
|
||||||
|
)
|
||||||
|
$: fields = viewModel && Object.keys(viewModel.schema)
|
||||||
|
|
||||||
|
function saveView() {
|
||||||
|
backendUiStore.actions.views.save(view)
|
||||||
|
notifier.success(`View ${view.name} saved.`)
|
||||||
|
onClosed()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="actions">
|
||||||
|
<h5>Group</h5>
|
||||||
|
<div class="input-group-row">
|
||||||
|
<p>By</p>
|
||||||
|
<Select secondary thin bind:value={view.groupBy}>
|
||||||
|
<option value="">Choose an option</option>
|
||||||
|
{#each fields as field}
|
||||||
|
<option value={field}>{field}</option>
|
||||||
|
{/each}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
<div class="footer">
|
||||||
|
<Button secondary on:click={onClosed}>Cancel</Button>
|
||||||
|
<Button primary on:click={saveView}>Save</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.actions {
|
||||||
|
display: grid;
|
||||||
|
grid-gap: var(--spacing-xl);
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: var(--spacing-m);
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 20px 1fr;
|
||||||
|
gap: var(--spacing-s);
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
font-size: var(--font-size-xs);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,27 +0,0 @@
|
||||||
<script>
|
|
||||||
import { DropdownMenu, TextButton as Button, Icon } from "@budibase/bbui"
|
|
||||||
import CreateEditRecord from "./CreateEditRecord.svelte"
|
|
||||||
import { Modal } from "components/common/Modal"
|
|
||||||
|
|
||||||
let anchor
|
|
||||||
let dropdown
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div bind:this={anchor}>
|
|
||||||
<Button text small on:click={dropdown.show}>
|
|
||||||
<Icon name="addrow" />
|
|
||||||
Create New Row
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<Modal bind:this={dropdown}>
|
|
||||||
<h5>Add New Row</h5>
|
|
||||||
<CreateEditRecord onClosed={dropdown.hide} />
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
h5 {
|
|
||||||
padding: var(--spacing-xl) 0 0 var(--spacing-xl);
|
|
||||||
margin: 0;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,41 +1,33 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext } from "svelte"
|
|
||||||
import { backendUiStore } from "builderStore"
|
import { backendUiStore } from "builderStore"
|
||||||
import {
|
import { DropdownMenu, Icon } from "@budibase/bbui"
|
||||||
DropdownMenu,
|
import CreateEditRecordModal from "../modals/CreateEditRecordModal.svelte"
|
||||||
Button,
|
|
||||||
Icon,
|
|
||||||
Input,
|
|
||||||
Select,
|
|
||||||
Heading,
|
|
||||||
} from "@budibase/bbui"
|
|
||||||
import { FIELDS } from "constants/backend"
|
|
||||||
import CreateEditRecordModal from "./CreateEditRecord.svelte"
|
|
||||||
import * as api from "../api"
|
import * as api from "../api"
|
||||||
import { notifier } from "builderStore/store/notifications"
|
import { notifier } from "builderStore/store/notifications"
|
||||||
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
||||||
|
import { Modal } from "components/common/Modal"
|
||||||
|
|
||||||
export let row
|
export let row
|
||||||
|
|
||||||
let anchor
|
let anchor
|
||||||
let dropdown
|
let dropdown
|
||||||
let editing
|
|
||||||
let confirmDeleteDialog
|
let confirmDeleteDialog
|
||||||
|
let modal
|
||||||
|
|
||||||
function showEditor() {
|
function showModal() {
|
||||||
editing = true
|
dropdown.hide()
|
||||||
|
modal.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideEditor() {
|
function showDelete() {
|
||||||
dropdown.hide()
|
dropdown.hide()
|
||||||
editing = false
|
confirmDeleteDialog.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteRow() {
|
async function deleteRow() {
|
||||||
await api.deleteRecord(row)
|
await api.deleteRecord(row)
|
||||||
notifier.success("Record deleted")
|
notifier.success("Record deleted")
|
||||||
backendUiStore.actions.records.delete(row)
|
backendUiStore.actions.records.delete(row)
|
||||||
hideEditor()
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -43,21 +35,16 @@
|
||||||
<i class="ri-more-line" />
|
<i class="ri-more-line" />
|
||||||
</div>
|
</div>
|
||||||
<DropdownMenu bind:this={dropdown} {anchor} align="left">
|
<DropdownMenu bind:this={dropdown} {anchor} align="left">
|
||||||
{#if editing}
|
<ul>
|
||||||
<h5>Edit Row</h5>
|
<li data-cy="edit-row" on:click={showModal}>
|
||||||
<CreateEditRecordModal onClosed={hideEditor} record={row} />
|
<Icon name="edit" />
|
||||||
{:else}
|
<span>Edit</span>
|
||||||
<ul>
|
</li>
|
||||||
<li data-cy="edit-row" on:click={showEditor}>
|
<li data-cy="delete-row" on:click={showDelete}>
|
||||||
<Icon name="edit" />
|
<Icon name="delete" />
|
||||||
<span>Edit</span>
|
<span>Delete</span>
|
||||||
</li>
|
</li>
|
||||||
<li data-cy="delete-row" on:click={() => confirmDeleteDialog.show()}>
|
</ul>
|
||||||
<Icon name="delete" />
|
|
||||||
<span>Delete</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
{/if}
|
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
bind:this={confirmDeleteDialog}
|
bind:this={confirmDeleteDialog}
|
||||||
|
@ -65,6 +52,9 @@
|
||||||
okText="Delete Row"
|
okText="Delete Row"
|
||||||
onOk={deleteRow}
|
onOk={deleteRow}
|
||||||
title="Confirm Delete" />
|
title="Confirm Delete" />
|
||||||
|
<Modal bind:this={modal}>
|
||||||
|
<CreateEditRecordModal record={row} />
|
||||||
|
</Modal>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.ri-more-line:hover {
|
.ri-more-line:hover {
|
|
@ -24,6 +24,11 @@
|
||||||
editing = false
|
editing = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showModal() {
|
||||||
|
hideEditor()
|
||||||
|
confirmDeleteDialog.show()
|
||||||
|
}
|
||||||
|
|
||||||
async function deleteTable() {
|
async function deleteTable() {
|
||||||
await backendUiStore.actions.models.delete(table)
|
await backendUiStore.actions.models.delete(table)
|
||||||
notifier.success("Table deleted")
|
notifier.success("Table deleted")
|
||||||
|
@ -66,7 +71,7 @@
|
||||||
<Icon name="edit" />
|
<Icon name="edit" />
|
||||||
Edit
|
Edit
|
||||||
</li>
|
</li>
|
||||||
<li data-cy="delete-table" on:click={() => confirmDeleteDialog.show()}>
|
<li data-cy="delete-table" on:click={showModal}>
|
||||||
<Icon name="delete" />
|
<Icon name="delete" />
|
||||||
Delete
|
Delete
|
||||||
</li>
|
</li>
|
||||||
|
@ -78,7 +83,6 @@
|
||||||
body={`Are you sure you wish to delete the table '${table.name}'? Your data will be deleted and this action cannot be undone.`}
|
body={`Are you sure you wish to delete the table '${table.name}'? Your data will be deleted and this action cannot be undone.`}
|
||||||
okText="Delete Table"
|
okText="Delete Table"
|
||||||
onOk={deleteTable}
|
onOk={deleteTable}
|
||||||
onCancel={hideEditor}
|
|
||||||
title="Confirm Delete" />
|
title="Confirm Delete" />
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
|
@ -24,6 +24,11 @@
|
||||||
editing = false
|
editing = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showDelete() {
|
||||||
|
dropdown.hide()
|
||||||
|
confirmDeleteDialog.show()
|
||||||
|
}
|
||||||
|
|
||||||
async function save() {
|
async function save() {
|
||||||
await backendUiStore.actions.views.save({
|
await backendUiStore.actions.views.save({
|
||||||
originalName,
|
originalName,
|
||||||
|
@ -61,7 +66,7 @@
|
||||||
<Icon name="edit" />
|
<Icon name="edit" />
|
||||||
Edit
|
Edit
|
||||||
</li>
|
</li>
|
||||||
<li data-cy="delete-view" on:click={() => confirmDeleteDialog.show()}>
|
<li data-cy="delete-view" on:click={showDelete}>
|
||||||
<Icon name="delete" />
|
<Icon name="delete" />
|
||||||
Delete
|
Delete
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -1,19 +1,13 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext } from "svelte"
|
|
||||||
import { slide } from "svelte/transition"
|
|
||||||
import { Switcher } from "@budibase/bbui"
|
|
||||||
import { goto } from "@sveltech/routify"
|
import { goto } from "@sveltech/routify"
|
||||||
import { store, backendUiStore } from "builderStore"
|
import { backendUiStore } from "builderStore"
|
||||||
import ListItem from "./ListItem.svelte"
|
import ListItem from "./ListItem.svelte"
|
||||||
import { Button } from "@budibase/bbui"
|
|
||||||
import CreateTablePopover from "./CreateTable.svelte"
|
import CreateTablePopover from "./CreateTable.svelte"
|
||||||
import EditTablePopover from "./EditTable.svelte"
|
import EditTablePopover from "./EditTable.svelte"
|
||||||
import EditViewPopover from "./EditView.svelte"
|
import EditViewPopover from "./EditView.svelte"
|
||||||
import { Heading } from "@budibase/bbui"
|
import { Heading } from "@budibase/bbui"
|
||||||
import { Spacer } from "@budibase/bbui"
|
import { Spacer } from "@budibase/bbui"
|
||||||
|
|
||||||
const { open, close } = getContext("simple-modal")
|
|
||||||
|
|
||||||
$: selectedView =
|
$: selectedView =
|
||||||
$backendUiStore.selectedView && $backendUiStore.selectedView.name
|
$backendUiStore.selectedView && $backendUiStore.selectedView.name
|
||||||
|
|
||||||
|
|
|
@ -1,64 +1,31 @@
|
||||||
<script>
|
<script>
|
||||||
import { Modal, Button, Heading, Spacer } from "@budibase/bbui"
|
import { Modal, ModalTitle, ModalFooter } from "components/common/Modal"
|
||||||
|
|
||||||
export let title = ""
|
export let title = ""
|
||||||
export let body = ""
|
export let body
|
||||||
export let okText = "OK"
|
export let okText
|
||||||
export let cancelText = "Cancel"
|
export let cancelText
|
||||||
export let onOk = () => {}
|
export let onOk
|
||||||
export let onCancel = () => {}
|
export let onCancel
|
||||||
|
|
||||||
|
let modal
|
||||||
|
|
||||||
export const show = () => {
|
export const show = () => {
|
||||||
theModal.show()
|
modal.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
export const hide = () => {
|
export const hide = () => {
|
||||||
theModal.hide()
|
modal.hide()
|
||||||
}
|
|
||||||
|
|
||||||
let theModal
|
|
||||||
|
|
||||||
const cancel = () => {
|
|
||||||
hide()
|
|
||||||
onCancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
const ok = () => {
|
|
||||||
const result = onOk()
|
|
||||||
// allow caller to return false, to cancel the "ok"
|
|
||||||
if (result === false) return
|
|
||||||
hide()
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Modal id={title} bind:this={theModal}>
|
<Modal id={title} bind:this={modal} on:hide={onCancel}>
|
||||||
<h2>{title}</h2>
|
<ModalTitle>{title}</ModalTitle>
|
||||||
<Spacer extraLarge />
|
<div class="body">{body}</div>
|
||||||
<div class="content">
|
<ModalFooter confirmText={okText} {cancelText} onConfirm={onOk} red />
|
||||||
<slot class="rows">{body}</slot>
|
|
||||||
</div>
|
|
||||||
<Spacer extraLarge />
|
|
||||||
<div class="modal-footer">
|
|
||||||
<Button red wide on:click={ok}>{okText}</Button>
|
|
||||||
<Button secondary wide on:click={cancel}>{cancelText}</Button>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
h2 {
|
.body {
|
||||||
font-size: var(--font-size-xl);
|
|
||||||
margin: 0;
|
|
||||||
font-family: var(--font-sans);
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-footer {
|
|
||||||
display: grid;
|
|
||||||
grid-gap: var(--spacing-s);
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
white-space: normal;
|
|
||||||
font-size: var(--font-size-s);
|
font-size: var(--font-size-s);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
const FETCH_RECORDS_URL = `/api/${linkedModelId}/records`
|
const FETCH_RECORDS_URL = `/api/${linkedModelId}/records`
|
||||||
const response = await api.get(FETCH_RECORDS_URL)
|
const response = await api.get(FETCH_RECORDS_URL)
|
||||||
const result = await response.json()
|
const result = await response.json()
|
||||||
console.log(result)
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,23 @@
|
||||||
<script>
|
<script>
|
||||||
import { createEventDispatcher } from "svelte"
|
/**
|
||||||
|
* Confirmation is handled as a callback rather than an event to allow
|
||||||
|
* handling the result - meaning a parent can prevent the modal closing.
|
||||||
|
*
|
||||||
|
* A show/hide API is exposed as part of the modal and also via context for
|
||||||
|
* children inside the modal.
|
||||||
|
* "show" and "hide" events are emitted as visibility changes.
|
||||||
|
*
|
||||||
|
* Modals are rendered at the top of the DOM tree.
|
||||||
|
*/
|
||||||
|
import { createEventDispatcher, setContext } from "svelte"
|
||||||
import { fade, fly } from "svelte/transition"
|
import { fade, fly } from "svelte/transition"
|
||||||
import { portal } from "./portal"
|
import { portal } from "./portal"
|
||||||
|
import { ContextKey } from "./context"
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
export let visible = false
|
let visible
|
||||||
export let cancelText = "Cancel"
|
|
||||||
export let confirmText = "Confirm"
|
|
||||||
export let showCancelButton = true
|
|
||||||
export let showConfirmButton = true
|
|
||||||
|
|
||||||
export function show() {
|
export function show() {
|
||||||
console.log("show")
|
|
||||||
if (visible) {
|
if (visible) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -26,6 +32,8 @@
|
||||||
visible = false
|
visible = false
|
||||||
dispatch("hide")
|
dispatch("hide")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setContext(ContextKey, { show, hide })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if visible}
|
{#if visible}
|
||||||
|
@ -66,7 +74,6 @@
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background-color: rgba(0, 0, 0, 0.25);
|
background-color: rgba(0, 0, 0, 0.25);
|
||||||
z-index: 999;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.scroll-wrapper {
|
.scroll-wrapper {
|
||||||
|
@ -98,5 +105,7 @@
|
||||||
flex: 0 0 400px;
|
flex: 0 0 400px;
|
||||||
margin: 2rem 0;
|
margin: 2rem 0;
|
||||||
border-radius: var(--border-radius-m);
|
border-radius: var(--border-radius-m);
|
||||||
|
gap: var(--spacing-xl);
|
||||||
|
padding: var(--spacing-xl);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -5,5 +5,6 @@
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
z-index: 999;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
<script>
|
||||||
|
import { getContext } from "svelte"
|
||||||
|
import { Button } from "@budibase/bbui"
|
||||||
|
import { ContextKey } from "./context"
|
||||||
|
|
||||||
|
export let cancelText = "Cancel"
|
||||||
|
export let confirmText = "Confirm"
|
||||||
|
export let showCancelButton = true
|
||||||
|
export let showConfirmButton = true
|
||||||
|
export let onConfirm
|
||||||
|
|
||||||
|
const modalContext = getContext(ContextKey)
|
||||||
|
|
||||||
|
function hide() {
|
||||||
|
modalContext.hide()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function confirm() {
|
||||||
|
if (!onConfirm || (await onConfirm()) !== false) {
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if showCancelButton || showConfirmButton}
|
||||||
|
<footer>
|
||||||
|
{#if showCancelButton}
|
||||||
|
<Button secondary on:click={hide}>{cancelText}</Button>
|
||||||
|
{/if}
|
||||||
|
{#if showConfirmButton}
|
||||||
|
<Button primary {...$$restProps} on:click={confirm}>{confirmText}</Button>
|
||||||
|
{/if}
|
||||||
|
</footer>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
footer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--spacing-m);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,10 @@
|
||||||
|
<h5>
|
||||||
|
<slot />
|
||||||
|
</h5>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
h5 {
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1 @@
|
||||||
|
export const ContextKey = "budibase-modal"
|
|
@ -1,2 +1,5 @@
|
||||||
export { default as Modal } from "./Modal.svelte"
|
export { default as Modal } from "./Modal.svelte"
|
||||||
export { default as ModalContainer } from "./ModalContainer.svelte"
|
export { default as ModalContainer } from "./ModalContainer.svelte"
|
||||||
|
export { default as ModalTitle } from "./ModalTitle.svelte"
|
||||||
|
export { default as ModalFooter } from "./ModalFooter.svelte"
|
||||||
|
export { ContextKey } from "./context"
|
||||||
|
|
|
@ -1,13 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext } from "svelte"
|
import ModelDataTable from "components/backend/DataTable/ModelDataTable.svelte"
|
||||||
import { Button } from "@budibase/bbui"
|
|
||||||
import ModelDataTable from "components/backend/DataTable"
|
|
||||||
import { backendUiStore } from "builderStore"
|
import { backendUiStore } from "builderStore"
|
||||||
import ActionButton from "components/common/ActionButton.svelte"
|
|
||||||
import * as api from "components/backend/DataTable/api"
|
|
||||||
import CreateEditRecordModal from "components/backend/DataTable/popovers/CreateEditRecord.svelte"
|
|
||||||
|
|
||||||
const { open, close } = getContext("simple-modal")
|
|
||||||
|
|
||||||
$: selectedModel = $backendUiStore.selectedModel
|
$: selectedModel = $backendUiStore.selectedModel
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,13 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext } from "svelte"
|
|
||||||
import { Button } from "@budibase/bbui"
|
|
||||||
import ViewDataTable from "components/backend/DataTable/ViewDataTable"
|
import ViewDataTable from "components/backend/DataTable/ViewDataTable"
|
||||||
import { backendUiStore } from "builderStore"
|
import { backendUiStore } from "builderStore"
|
||||||
import ActionButton from "components/common/ActionButton.svelte"
|
|
||||||
import * as api from "components/backend/DataTable/api"
|
|
||||||
import CreateEditRecord from "components/backend/DataTable/popovers/CreateEditRecord.svelte"
|
|
||||||
|
|
||||||
const { open, close } = getContext("simple-modal")
|
|
||||||
|
|
||||||
$: selectedView = $backendUiStore.selectedView
|
$: selectedView = $backendUiStore.selectedView
|
||||||
</script>
|
</script>
|
||||||
|
|
Loading…
Reference in New Issue