2023-01-10 17:28:27 +01:00
|
|
|
<script>
|
2023-02-15 19:27:51 +01:00
|
|
|
import Details from '$components/details.svelte';
|
|
|
|
import Grid from '$components/grid.svelte';
|
|
|
|
import Icon from '$components/icon.svelte';
|
|
|
|
import ObjectViewer from '$components/objectviewer.svelte';
|
|
|
|
import { randomString } from '$lib/math';
|
|
|
|
import { inputTypes } from '$lib/mongo';
|
|
|
|
import views from '$lib/stores/views';
|
2023-02-19 17:26:32 +01:00
|
|
|
import { capitalise, convertLooseJson, jsonLooseParse } from '$lib/strings';
|
2023-02-15 19:27:51 +01:00
|
|
|
import { InsertItems } from '$wails/go/app/App';
|
|
|
|
import { EJSON } from 'bson';
|
2023-05-29 20:51:54 +02:00
|
|
|
import { createEventDispatcher, onMount } from 'svelte';
|
2023-01-29 20:00:15 +01:00
|
|
|
import Form from './components/form.svelte';
|
2023-05-29 20:51:54 +02:00
|
|
|
import ObjectEditor from '$components/objecteditor.svelte';
|
2023-01-10 17:28:27 +01:00
|
|
|
|
|
|
|
export let collection;
|
|
|
|
|
2023-01-13 16:56:48 +01:00
|
|
|
const dispatch = createEventDispatcher();
|
2023-01-31 21:12:31 +01:00
|
|
|
const formValidity = {};
|
2023-02-15 19:53:49 +01:00
|
|
|
|
2023-05-29 20:51:54 +02:00
|
|
|
let editor;
|
2023-01-13 16:56:48 +01:00
|
|
|
let json = '';
|
2023-01-29 20:00:15 +01:00
|
|
|
let newItems = [];
|
2023-01-10 17:28:27 +01:00
|
|
|
let insertedIds;
|
2023-01-29 20:00:15 +01:00
|
|
|
let objectViewerData = '';
|
|
|
|
let viewType = 'form';
|
2023-01-31 21:12:31 +01:00
|
|
|
let allValid = false;
|
2023-02-15 19:53:49 +01:00
|
|
|
|
2023-01-29 20:00:15 +01:00
|
|
|
$: viewsForCollection = views.forCollection(collection.hostKey, collection.dbKey, collection.key);
|
|
|
|
$: oppositeViewType = viewType === 'table' ? 'form' : 'table';
|
2023-01-31 21:12:31 +01:00
|
|
|
$: allValid = Object.values(formValidity).every(v => v !== false);
|
2023-01-10 17:28:27 +01:00
|
|
|
|
2023-01-31 21:12:31 +01:00
|
|
|
$: {
|
|
|
|
if (collection.viewKey === 'list') {
|
|
|
|
try {
|
2023-02-19 17:26:32 +01:00
|
|
|
newItems = EJSON.deserialize(jsonLooseParse(json), { relaxed: false });
|
2023-01-31 21:12:31 +01:00
|
|
|
}
|
|
|
|
catch { /* ok */ }
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
json = EJSON.stringify(newItems, undefined, 2, { relaxed: false });
|
|
|
|
}
|
2023-01-29 20:16:31 +01:00
|
|
|
}
|
|
|
|
|
2023-02-15 19:53:49 +01:00
|
|
|
$: if ((viewType === 'form') && !newItems?.length) {
|
|
|
|
newItems = [ {} ];
|
|
|
|
}
|
|
|
|
|
2023-01-10 17:28:27 +01:00
|
|
|
async function insert() {
|
2023-05-29 20:51:54 +02:00
|
|
|
insertedIds = await InsertItems(
|
|
|
|
collection.hostKey,
|
|
|
|
collection.dbKey,
|
|
|
|
collection.key,
|
|
|
|
convertLooseJson(json)
|
|
|
|
);
|
2023-01-29 20:16:31 +01:00
|
|
|
if ((collection.viewKey === 'list') && insertedIds) {
|
|
|
|
newItems = [];
|
2023-01-29 20:00:15 +01:00
|
|
|
}
|
2023-01-13 16:56:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
function showDocs() {
|
|
|
|
dispatch('performFind', {
|
|
|
|
query: insertedIds.length === 1
|
|
|
|
? `{ "_id": ${JSON.stringify(insertedIds[0])} }`
|
|
|
|
: `{ "_id": { "$in": [ ${insertedIds.map(id => JSON.stringify(id)).join(', ')} ] } }`,
|
|
|
|
});
|
2023-01-10 17:28:27 +01:00
|
|
|
}
|
2023-01-29 20:00:15 +01:00
|
|
|
|
|
|
|
function switchViewType() {
|
|
|
|
viewType = oppositeViewType;
|
|
|
|
}
|
|
|
|
|
|
|
|
function showJson() {
|
|
|
|
if (viewType === 'form') {
|
|
|
|
objectViewerData = { ...(newItems[0] || {}) };
|
|
|
|
}
|
|
|
|
else if (viewType === 'table') {
|
|
|
|
objectViewerData = [ ...newItems ];
|
|
|
|
}
|
|
|
|
}
|
2023-01-31 16:58:23 +01:00
|
|
|
|
2023-02-15 19:53:49 +01:00
|
|
|
function addRow(beforeIndex = -1) {
|
|
|
|
if ((beforeIndex === -1) || (typeof beforeIndex !== 'number')) {
|
|
|
|
newItems = [ ...newItems, {} ];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
newItems = [
|
|
|
|
...newItems.slice(0, beforeIndex),
|
|
|
|
{},
|
|
|
|
...newItems.slice(beforeIndex + 1),
|
|
|
|
];
|
|
|
|
}
|
2023-01-31 16:58:23 +01:00
|
|
|
}
|
2023-01-31 21:12:31 +01:00
|
|
|
|
|
|
|
function deleteRow(index) {
|
|
|
|
newItems.splice(index, 1);
|
|
|
|
newItems = newItems;
|
|
|
|
}
|
2023-05-29 20:51:54 +02:00
|
|
|
|
|
|
|
onMount(() => {
|
|
|
|
if (collection.viewKey === 'list') {
|
|
|
|
editor.dispatch({
|
|
|
|
changes: {
|
|
|
|
from: 0,
|
|
|
|
to: editor.state.doc.length,
|
|
|
|
insert: '{\n\t\n}',
|
|
|
|
},
|
|
|
|
selection: {
|
|
|
|
anchor: 3,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
editor.focus();
|
|
|
|
}
|
|
|
|
});
|
2023-01-10 17:28:27 +01:00
|
|
|
</script>
|
|
|
|
|
|
|
|
<form on:submit|preventDefault={insert}>
|
2023-01-31 21:12:31 +01:00
|
|
|
<div class="items">
|
|
|
|
{#if collection.viewKey === 'list'}
|
2023-05-29 20:51:54 +02:00
|
|
|
<!-- svelte-ignore a11y-label-has-associated-control -->
|
2023-02-19 17:26:32 +01:00
|
|
|
<label class="field json">
|
2023-05-29 20:51:54 +02:00
|
|
|
<ObjectEditor bind:text={json} bind:editor />
|
2023-01-31 21:12:31 +01:00
|
|
|
</label>
|
|
|
|
{:else if viewType === 'form'}
|
|
|
|
<div class="form">
|
|
|
|
{#each newItems as item, index}
|
|
|
|
<Details
|
|
|
|
title="Item #{index + 1} {(item._id !== undefined) ? `(${item._id})` : ''}"
|
|
|
|
initiallyOpen={index === 0}
|
|
|
|
deletable={true}
|
|
|
|
on:delete={() => deleteRow(index)}
|
|
|
|
>
|
|
|
|
<fieldset>
|
2023-02-15 19:53:49 +01:00
|
|
|
<Form
|
|
|
|
bind:item={newItems[index]}
|
|
|
|
bind:valid={formValidity[index]}
|
|
|
|
view={$views[collection.viewKey]}
|
|
|
|
emptyHint="This form has no fields. Use the view configurator in the bottom right corner to add fields."
|
|
|
|
/>
|
2023-01-31 21:12:31 +01:00
|
|
|
</fieldset>
|
|
|
|
</Details>
|
|
|
|
{/each}
|
|
|
|
</div>
|
|
|
|
{:else if viewType === 'table'}
|
|
|
|
<div class="table">
|
|
|
|
<Grid
|
|
|
|
key="id"
|
|
|
|
items={newItems}
|
|
|
|
columns={
|
|
|
|
$views[collection.viewKey]?.columns
|
|
|
|
?.filter(c => inputTypes.includes(c.inputType))
|
|
|
|
.map(c => ({ ...c, id: randomString(8), title: c.key })) || []
|
|
|
|
}
|
|
|
|
showHeaders={true}
|
|
|
|
canSelect={false}
|
|
|
|
canRemoveItems={true}
|
|
|
|
hideChildrenToggles={true}
|
|
|
|
on:addRow={addRow}
|
|
|
|
bind:inputsValid={allValid}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
{/if}
|
2023-02-15 19:53:49 +01:00
|
|
|
|
|
|
|
{#if collection.viewKey !== 'list'}
|
2023-05-30 19:40:12 +02:00
|
|
|
<button class="button-small" type="button" on:click={addRow}>
|
2023-02-15 19:53:49 +01:00
|
|
|
<Icon name="+" /> Add item
|
|
|
|
</button>
|
|
|
|
{/if}
|
2023-01-31 21:12:31 +01:00
|
|
|
</div>
|
2023-01-10 17:28:27 +01:00
|
|
|
|
|
|
|
<div class="flex">
|
|
|
|
<div>
|
|
|
|
{#if insertedIds}
|
2023-01-13 16:56:48 +01:00
|
|
|
<span class="flash-green">Success! {insertedIds.length} document{insertedIds.length > 1 ? 's' : ''} inserted</span>
|
|
|
|
{/if}
|
|
|
|
</div>
|
|
|
|
<div>
|
|
|
|
{#if insertedIds}
|
|
|
|
<button class="btn" type="button" on:click={showDocs}>View inserted docs</button>
|
2023-01-10 17:28:27 +01:00
|
|
|
{/if}
|
2023-01-29 20:00:15 +01:00
|
|
|
{#if collection.viewKey !== 'list'}
|
|
|
|
<button class="btn" type="button" on:click={showJson} title="Show JSON">
|
|
|
|
<Icon name="code" />
|
|
|
|
</button>
|
|
|
|
<button class="btn" type="button" on:click={switchViewType} title="Edit as {oppositeViewType}">
|
2023-02-15 19:53:49 +01:00
|
|
|
<Icon name={oppositeViewType} /> {capitalise(oppositeViewType)}
|
2023-01-29 20:00:15 +01:00
|
|
|
</button>
|
|
|
|
{/if}
|
|
|
|
<label class="field inline">
|
|
|
|
<select bind:value={collection.viewKey}>
|
|
|
|
{#each Object.entries(viewsForCollection) as [key, view]}
|
|
|
|
<option value={key}>{key === 'list' ? 'Raw JSON' : view.name}</option>
|
|
|
|
{/each}
|
|
|
|
</select>
|
|
|
|
<button class="btn" type="button" on:click={() => dispatch('openViewConfig')} title="Configure view">
|
|
|
|
<Icon name="cog" />
|
|
|
|
</button>
|
|
|
|
</label>
|
2023-01-31 21:12:31 +01:00
|
|
|
<button type="submit" class="btn" disabled={$views[collection.viewKey]?.type === 'list' ? !json : !allValid}>
|
2023-01-20 13:54:57 +01:00
|
|
|
<Icon name="+" /> Insert
|
|
|
|
</button>
|
2023-01-10 17:28:27 +01:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</form>
|
|
|
|
|
2023-01-29 20:00:15 +01:00
|
|
|
<ObjectViewer data={objectViewerData} />
|
|
|
|
|
2023-01-10 17:28:27 +01:00
|
|
|
<style>
|
2023-01-13 16:56:48 +01:00
|
|
|
form {
|
|
|
|
display: grid;
|
2023-01-31 21:12:31 +01:00
|
|
|
grid-template: 1fr auto / 1fr;
|
2023-01-13 16:56:48 +01:00
|
|
|
gap: 0.5rem;
|
|
|
|
}
|
|
|
|
|
2023-01-31 21:12:31 +01:00
|
|
|
.items {
|
|
|
|
overflow: auto;
|
|
|
|
}
|
|
|
|
.items .table {
|
2023-01-31 16:58:23 +01:00
|
|
|
background-color: #fff;
|
|
|
|
border: 1px solid #ccc;
|
|
|
|
}
|
|
|
|
|
2023-01-10 17:28:27 +01:00
|
|
|
.flex {
|
|
|
|
display: flex;
|
|
|
|
justify-content: space-between;
|
|
|
|
}
|
2023-02-19 17:26:32 +01:00
|
|
|
|
|
|
|
.field.json {
|
|
|
|
height: 100%;
|
|
|
|
}
|
2023-01-10 17:28:27 +01:00
|
|
|
</style>
|