added ImportPopup
This commit is contained in:
parent
93ab5fbea2
commit
4e58e7ad6a
|
@ -169,6 +169,7 @@ func (api *collectionApi) delete(c echo.Context) error {
|
||||||
return handlerErr
|
return handlerErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @todo add event
|
||||||
func (api *collectionApi) bulkImport(c echo.Context) error {
|
func (api *collectionApi) bulkImport(c echo.Context) error {
|
||||||
form := forms.NewCollectionsImport(api.app)
|
form := forms.NewCollectionsImport(api.app)
|
||||||
|
|
||||||
|
@ -182,5 +183,5 @@ func (api *collectionApi) bulkImport(c echo.Context) error {
|
||||||
return rest.NewBadRequestError("Failed to import the submitted collections.", submitErr)
|
return rest.NewBadRequestError("Failed to import the submitted collections.", submitErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return c.NoContent(http.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package forms
|
package forms
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
|
@ -59,24 +60,8 @@ func (form *CollectionsImport) Submit() error {
|
||||||
mappedOldCollections[old.GetId()] = old
|
mappedOldCollections[old.GetId()] = old
|
||||||
}
|
}
|
||||||
|
|
||||||
// raw insert/replace (aka. without any validations)
|
|
||||||
// (required to make sure that all linked collections exists before running the validations)
|
|
||||||
mappedFormCollections := make(map[string]*models.Collection, len(form.Collections))
|
mappedFormCollections := make(map[string]*models.Collection, len(form.Collections))
|
||||||
for _, collection := range form.Collections {
|
for _, collection := range form.Collections {
|
||||||
if mappedOldCollections[collection.GetId()] == nil {
|
|
||||||
collection.MarkAsNew()
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := txDao.Save(collection); err != nil {
|
|
||||||
if form.app.IsDebug() {
|
|
||||||
log.Println("[CollectionsImport] Save failure", collection.Name, err)
|
|
||||||
}
|
|
||||||
return validation.Errors{"collections": validation.NewError(
|
|
||||||
"collections_import_save_failure",
|
|
||||||
fmt.Sprintf("Failed to save the imported collection %q (id: %s).", collection.Name, collection.Id),
|
|
||||||
)}
|
|
||||||
}
|
|
||||||
|
|
||||||
mappedFormCollections[collection.GetId()] = collection
|
mappedFormCollections[collection.GetId()] = collection
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,15 +74,33 @@ func (form *CollectionsImport) Submit() error {
|
||||||
if form.app.IsDebug() {
|
if form.app.IsDebug() {
|
||||||
log.Println("[CollectionsImport] DeleteOthers failure", old.Name, err)
|
log.Println("[CollectionsImport] DeleteOthers failure", old.Name, err)
|
||||||
}
|
}
|
||||||
return validation.Errors{"deleteOthers": validation.NewError(
|
return validation.Errors{"collections": validation.NewError(
|
||||||
"collections_import_collection_delete_failure",
|
"collections_import_collection_delete_failure",
|
||||||
fmt.Sprintf("Failed to delete collection %q. Make sure that the collection is not system or referenced by other collections.", old.Name),
|
fmt.Sprintf("Failed to delete collection %q (%s). Make sure that the collection is not system or referenced by other collections.", old.Name, old.Id),
|
||||||
)}
|
)}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// raw insert/replace (aka. without any validations)
|
||||||
|
// (required to make sure that all linked collections exists before running the validations)
|
||||||
|
for _, collection := range form.Collections {
|
||||||
|
if mappedOldCollections[collection.GetId()] == nil {
|
||||||
|
collection.MarkAsNew()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := txDao.Save(collection); err != nil {
|
||||||
|
if form.app.IsDebug() {
|
||||||
|
log.Println("[CollectionsImport] Save failure", collection.Name, err)
|
||||||
|
}
|
||||||
|
return validation.Errors{"collections": validation.NewError(
|
||||||
|
"collections_import_save_failure",
|
||||||
|
fmt.Sprintf("Integrity constraints failed - the collection %q (%s) cannot be imported.", collection.Name, collection.Id),
|
||||||
|
)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// refresh the actual persisted collections list
|
// refresh the actual persisted collections list
|
||||||
refreshedCollections := []*models.Collection{}
|
refreshedCollections := []*models.Collection{}
|
||||||
if err := txDao.CollectionQuery().All(&refreshedCollections); err != nil {
|
if err := txDao.CollectionQuery().All(&refreshedCollections); err != nil {
|
||||||
|
@ -125,9 +128,13 @@ func (form *CollectionsImport) Submit() error {
|
||||||
if form.app.IsDebug() {
|
if form.app.IsDebug() {
|
||||||
log.Println("[CollectionsImport] Validate failure", collection.Name, err)
|
log.Println("[CollectionsImport] Validate failure", collection.Name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// serialize the validation error(s)
|
||||||
|
serializedErr, _ := json.Marshal(err)
|
||||||
|
|
||||||
return validation.Errors{"collections": validation.NewError(
|
return validation.Errors{"collections": validation.NewError(
|
||||||
"collections_import_validate_failure",
|
"collections_import_validate_failure",
|
||||||
fmt.Sprintf("Integrity check failed - collection %q has invalid data.", collection.Name),
|
fmt.Sprintf("Data validations failed for collection %q (%s): %s", collection.Name, collection.Id, serializedErr),
|
||||||
)}
|
)}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
"@codemirror/autocomplete": "^6.0.0",
|
"@codemirror/autocomplete": "^6.0.0",
|
||||||
"@codemirror/commands": "^6.0.0",
|
"@codemirror/commands": "^6.0.0",
|
||||||
"@codemirror/lang-javascript": "^6.0.2",
|
"@codemirror/lang-javascript": "^6.0.2",
|
||||||
"@codemirror/lang-json": "^6.0.0",
|
|
||||||
"@codemirror/language": "^6.0.0",
|
"@codemirror/language": "^6.0.0",
|
||||||
"@codemirror/legacy-modes": "^6.0.0",
|
"@codemirror/legacy-modes": "^6.0.0",
|
||||||
"@codemirror/search": "^6.0.0",
|
"@codemirror/search": "^6.0.0",
|
||||||
|
@ -29,8 +28,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"../../js-sdk": {
|
"../../js-sdk": {
|
||||||
"name": "pocketbase",
|
"version": "0.3.1",
|
||||||
"version": "0.3.0",
|
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -101,16 +99,6 @@
|
||||||
"@lezer/javascript": "^1.0.0"
|
"@lezer/javascript": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@codemirror/lang-json": {
|
|
||||||
"version": "6.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.0.tgz",
|
|
||||||
"integrity": "sha512-DvTcYTKLmg2viADXlTdufrT334M9jowe1qO02W28nvm+nejcvhM5vot5mE8/kPrxYw/HJHhwu1z2PyBpnMLCNQ==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@codemirror/language": "^6.0.0",
|
|
||||||
"@lezer/json": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@codemirror/language": {
|
"node_modules/@codemirror/language": {
|
||||||
"version": "6.2.1",
|
"version": "6.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.2.1.tgz",
|
||||||
|
@ -163,9 +151,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@codemirror/view": {
|
"node_modules/@codemirror/view": {
|
||||||
"version": "6.1.4",
|
"version": "6.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.2.0.tgz",
|
||||||
"integrity": "sha512-pekgUX+0hL4ri2JV7/bu7jhhwOgOhU1eRc1/ZyAQYCWcCI4TPB1qLrPE3cD/qW9yjBcYiN9MN0XI1tjK7Yw05Q==",
|
"integrity": "sha512-3emW1symh+GoteFMBPsltjmF790U/trouLILATh3JodbF/z98HvcQh2g3+H6dfNIHx16uNonsAF4mNzVr1TJNA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/state": "^6.0.0",
|
"@codemirror/state": "^6.0.0",
|
||||||
|
@ -214,16 +202,6 @@
|
||||||
"@lezer/lr": "^1.0.0"
|
"@lezer/lr": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@lezer/json": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-zbAuUY09RBzCoCA3lJ1+ypKw5WSNvLqGMtasdW6HvVOqZoCpPr8eWrsGnOVWGKGn8Rh21FnrKRVlJXrGAVUqRw==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@lezer/highlight": "^1.0.0",
|
|
||||||
"@lezer/lr": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@lezer/lr": {
|
"node_modules/@lezer/lr": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.2.1.tgz",
|
||||||
|
@ -1038,9 +1016,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/sass": {
|
"node_modules/sass": {
|
||||||
"version": "1.54.2",
|
"version": "1.54.3",
|
||||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.54.2.tgz",
|
"resolved": "https://registry.npmjs.org/sass/-/sass-1.54.3.tgz",
|
||||||
"integrity": "sha512-wbVV26sejsCIbBScZZtNkvnrB/bVCQ8hSlZ01D9nzsVh9zLqCkWrlpvTb3YEb6xsuNi9cx75hncqwikHFSz7tw==",
|
"integrity": "sha512-fLodey5Qd41Pxp/Tk7Al97sViYwF/TazRc5t6E65O7JOk4XF8pzwIW7CvCxYVOfJFFI/1x5+elDyBIixrp+zrw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chokidar": ">=3.0.0 <4.0.0",
|
"chokidar": ">=3.0.0 <4.0.0",
|
||||||
|
@ -1232,16 +1210,6 @@
|
||||||
"@lezer/javascript": "^1.0.0"
|
"@lezer/javascript": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@codemirror/lang-json": {
|
|
||||||
"version": "6.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.0.tgz",
|
|
||||||
"integrity": "sha512-DvTcYTKLmg2viADXlTdufrT334M9jowe1qO02W28nvm+nejcvhM5vot5mE8/kPrxYw/HJHhwu1z2PyBpnMLCNQ==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"@codemirror/language": "^6.0.0",
|
|
||||||
"@lezer/json": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@codemirror/language": {
|
"@codemirror/language": {
|
||||||
"version": "6.2.1",
|
"version": "6.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.2.1.tgz",
|
||||||
|
@ -1294,9 +1262,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@codemirror/view": {
|
"@codemirror/view": {
|
||||||
"version": "6.1.4",
|
"version": "6.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.2.0.tgz",
|
||||||
"integrity": "sha512-pekgUX+0hL4ri2JV7/bu7jhhwOgOhU1eRc1/ZyAQYCWcCI4TPB1qLrPE3cD/qW9yjBcYiN9MN0XI1tjK7Yw05Q==",
|
"integrity": "sha512-3emW1symh+GoteFMBPsltjmF790U/trouLILATh3JodbF/z98HvcQh2g3+H6dfNIHx16uNonsAF4mNzVr1TJNA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@codemirror/state": "^6.0.0",
|
"@codemirror/state": "^6.0.0",
|
||||||
|
@ -1336,16 +1304,6 @@
|
||||||
"@lezer/lr": "^1.0.0"
|
"@lezer/lr": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@lezer/json": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-zbAuUY09RBzCoCA3lJ1+ypKw5WSNvLqGMtasdW6HvVOqZoCpPr8eWrsGnOVWGKGn8Rh21FnrKRVlJXrGAVUqRw==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"@lezer/highlight": "^1.0.0",
|
|
||||||
"@lezer/lr": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@lezer/lr": {
|
"@lezer/lr": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.2.1.tgz",
|
||||||
|
@ -1855,9 +1813,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sass": {
|
"sass": {
|
||||||
"version": "1.54.2",
|
"version": "1.54.3",
|
||||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.54.2.tgz",
|
"resolved": "https://registry.npmjs.org/sass/-/sass-1.54.3.tgz",
|
||||||
"integrity": "sha512-wbVV26sejsCIbBScZZtNkvnrB/bVCQ8hSlZ01D9nzsVh9zLqCkWrlpvTb3YEb6xsuNi9cx75hncqwikHFSz7tw==",
|
"integrity": "sha512-fLodey5Qd41Pxp/Tk7Al97sViYwF/TazRc5t6E65O7JOk4XF8pzwIW7CvCxYVOfJFFI/1x5+elDyBIixrp+zrw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"chokidar": ">=3.0.0 <4.0.0",
|
"chokidar": ">=3.0.0 <4.0.0",
|
||||||
|
|
|
@ -1,134 +0,0 @@
|
||||||
<script>
|
|
||||||
import { onMount, createEventDispatcher } from "svelte";
|
|
||||||
// code mirror imports
|
|
||||||
// ---
|
|
||||||
import {
|
|
||||||
keymap,
|
|
||||||
highlightSpecialChars,
|
|
||||||
drawSelection,
|
|
||||||
dropCursor,
|
|
||||||
rectangularSelection,
|
|
||||||
EditorView,
|
|
||||||
placeholder as placeholderExt,
|
|
||||||
} from "@codemirror/view";
|
|
||||||
import { EditorState, Compartment } from "@codemirror/state";
|
|
||||||
import { defaultHighlightStyle, syntaxHighlighting, bracketMatching } from "@codemirror/language";
|
|
||||||
import { defaultKeymap, history, historyKeymap, indentWithTab } from "@codemirror/commands";
|
|
||||||
import { searchKeymap, highlightSelectionMatches } from "@codemirror/search";
|
|
||||||
import { closeBrackets, closeBracketsKeymap } from "@codemirror/autocomplete";
|
|
||||||
import { javascript } from "@codemirror/lang-javascript";
|
|
||||||
// ---
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
|
||||||
|
|
||||||
export let value = "";
|
|
||||||
export let disabled = false;
|
|
||||||
export let placeholder = "";
|
|
||||||
export let singleLine = false;
|
|
||||||
|
|
||||||
let editor;
|
|
||||||
let container;
|
|
||||||
let editableCompartment = new Compartment();
|
|
||||||
let readOnlyCompartment = new Compartment();
|
|
||||||
let placeholderCompartment = new Compartment();
|
|
||||||
|
|
||||||
$: if (editor && typeof disabled !== "undefined") {
|
|
||||||
editor.dispatch({
|
|
||||||
effects: [
|
|
||||||
editableCompartment.reconfigure(EditorView.editable.of(!disabled)),
|
|
||||||
readOnlyCompartment.reconfigure(EditorState.readOnly.of(disabled)),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
triggerNativeChange();
|
|
||||||
}
|
|
||||||
|
|
||||||
$: if (editor && value != editor.state.doc.toString()) {
|
|
||||||
editor.dispatch({
|
|
||||||
changes: {
|
|
||||||
from: 0,
|
|
||||||
to: editor.state.doc.length,
|
|
||||||
insert: value,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
$: if (editor && typeof placeholder !== "undefined") {
|
|
||||||
editor.dispatch({
|
|
||||||
effects: [placeholderCompartment.reconfigure(placeholderExt(placeholder))],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Focus the editor (if inited).
|
|
||||||
export function focus() {
|
|
||||||
editor?.focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Emulate native change event for the editor container element.
|
|
||||||
function triggerNativeChange() {
|
|
||||||
container?.dispatchEvent(
|
|
||||||
new CustomEvent("change", {
|
|
||||||
detail: { value },
|
|
||||||
bubbles: true,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
const submitShortcut = {
|
|
||||||
key: "Enter",
|
|
||||||
run: (_) => {
|
|
||||||
// trigger submit on enter for singleline input
|
|
||||||
if (singleLine) {
|
|
||||||
dispatch("submit", value);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
editor = new EditorView({
|
|
||||||
parent: container,
|
|
||||||
state: EditorState.create({
|
|
||||||
doc: value,
|
|
||||||
extensions: [
|
|
||||||
highlightSpecialChars(),
|
|
||||||
history(),
|
|
||||||
drawSelection(),
|
|
||||||
dropCursor(),
|
|
||||||
EditorState.allowMultipleSelections.of(true),
|
|
||||||
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
|
|
||||||
bracketMatching(),
|
|
||||||
closeBrackets(),
|
|
||||||
rectangularSelection(),
|
|
||||||
highlightSelectionMatches(),
|
|
||||||
keymap.of([
|
|
||||||
submitShortcut,
|
|
||||||
indentWithTab,
|
|
||||||
...closeBracketsKeymap,
|
|
||||||
...defaultKeymap,
|
|
||||||
...searchKeymap,
|
|
||||||
...historyKeymap,
|
|
||||||
]),
|
|
||||||
EditorView.lineWrapping,
|
|
||||||
javascript(),
|
|
||||||
placeholderCompartment.of(placeholderExt(placeholder)),
|
|
||||||
editableCompartment.of(EditorView.editable.of(true)),
|
|
||||||
readOnlyCompartment.of(EditorState.readOnly.of(false)),
|
|
||||||
EditorState.transactionFilter.of((tr) => {
|
|
||||||
return singleLine && tr.newDoc.lines > 1 ? [] : tr;
|
|
||||||
}),
|
|
||||||
EditorView.updateListener.of((v) => {
|
|
||||||
if (!v.docChanged || disabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
value = v.state.doc.toString();
|
|
||||||
triggerNativeChange();
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => editor?.destroy();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div bind:this={container} class="code-editor" />
|
|
|
@ -1,352 +0,0 @@
|
||||||
<script>
|
|
||||||
import ApiClient from "@/utils/ApiClient";
|
|
||||||
import CommonHelper from "@/utils/CommonHelper";
|
|
||||||
import Field from "@/components/base/Field.svelte";
|
|
||||||
import CodeBlock from "@/components/base/CodeBlock.svelte";
|
|
||||||
import { onMount } from "svelte";
|
|
||||||
|
|
||||||
const uniqueId = "exports_" + CommonHelper.randomString(5);
|
|
||||||
|
|
||||||
let collections = [];
|
|
||||||
let isLoadingCollections = false;
|
|
||||||
|
|
||||||
loadCollections();
|
|
||||||
|
|
||||||
async function loadCollections() {
|
|
||||||
isLoadingCollections = true;
|
|
||||||
|
|
||||||
try {
|
|
||||||
collections = await ApiClient.collections.getFullList(100, {
|
|
||||||
$cancelKey: uniqueId,
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
ApiClient.errorResponseHandler(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
isLoadingCollections = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let oldSchema = "";
|
|
||||||
let newSchema = "";
|
|
||||||
|
|
||||||
function diff_prettyHtml(diffs, showInsert) {
|
|
||||||
const html = [];
|
|
||||||
const pattern_amp = /&/g;
|
|
||||||
const pattern_lt = /</g;
|
|
||||||
const pattern_gt = />/g;
|
|
||||||
const pattern_para = /\n/g;
|
|
||||||
for (let x = 0; x < diffs.length; x++) {
|
|
||||||
let op = diffs[x][0]; // Operation (insert, delete, equal)
|
|
||||||
let data = diffs[x][1]; // Text of change.
|
|
||||||
let text = data
|
|
||||||
.replace(pattern_amp, "&")
|
|
||||||
.replace(pattern_lt, "<")
|
|
||||||
.replace(pattern_gt, ">")
|
|
||||||
.replace(pattern_para, "<br>");
|
|
||||||
// text = CommonHelper.stripTags(text);
|
|
||||||
switch (op) {
|
|
||||||
case DIFF_INSERT:
|
|
||||||
if (showInsert) {
|
|
||||||
html[x] = '<ins class="block">' + text + "</ins>";
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DIFF_DELETE:
|
|
||||||
if (!showInsert) {
|
|
||||||
html[x] = '<del class="block">' + text + "</del>";
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DIFF_EQUAL:
|
|
||||||
html[x] = "<span>" + text + "</span>";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return html.join("");
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
var dmp = new diff_match_patch();
|
|
||||||
const text1 = [
|
|
||||||
{
|
|
||||||
id: "zwWlxR46txtoAwx",
|
|
||||||
created: "2022-08-01 17:32:24.329",
|
|
||||||
updated: "2022-08-04 10:19:57.248",
|
|
||||||
name: "profia sdles",
|
|
||||||
system: true,
|
|
||||||
listRule: "userId = @request.user.id",
|
|
||||||
viewRule: "userId = @request.user.id",
|
|
||||||
createRule: "userId = @request.user.id",
|
|
||||||
updateRule: "userId = @request.user.id",
|
|
||||||
deleteRule: null,
|
|
||||||
schema: [
|
|
||||||
{
|
|
||||||
id: "nsght7oy",
|
|
||||||
name: "userId",
|
|
||||||
type: "user",
|
|
||||||
system: true,
|
|
||||||
required: true,
|
|
||||||
unique: true,
|
|
||||||
options: {
|
|
||||||
maxSelect: 1,
|
|
||||||
cascadeDelete: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "atpc4yjm",
|
|
||||||
name: "name",
|
|
||||||
type: "text",
|
|
||||||
system: false,
|
|
||||||
required: false,
|
|
||||||
unique: false,
|
|
||||||
options: {
|
|
||||||
min: null,
|
|
||||||
max: null,
|
|
||||||
pattern: "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "akb4s9de",
|
|
||||||
name: "avatar",
|
|
||||||
type: "file",
|
|
||||||
system: false,
|
|
||||||
required: false,
|
|
||||||
unique: false,
|
|
||||||
options: {
|
|
||||||
maxSelect: 1,
|
|
||||||
maxSize: 5242880,
|
|
||||||
mimeTypes: ["image/jpg", "image/jpeg", "image/png", "image/svg+xml", "image/gif"],
|
|
||||||
thumbs: null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "IV8FbE78jmXF56d",
|
|
||||||
created: "",
|
|
||||||
updated: "2022-08-04 10:21:54.100",
|
|
||||||
name: "abc",
|
|
||||||
system: false,
|
|
||||||
listRule: null,
|
|
||||||
viewRule: null,
|
|
||||||
createRule: null,
|
|
||||||
updateRule: null,
|
|
||||||
deleteRule: null,
|
|
||||||
schema: [
|
|
||||||
{
|
|
||||||
id: "t2pukeas",
|
|
||||||
name: "demo",
|
|
||||||
type: "text",
|
|
||||||
system: false,
|
|
||||||
required: false,
|
|
||||||
unique: false,
|
|
||||||
options: {
|
|
||||||
min: null,
|
|
||||||
max: null,
|
|
||||||
pattern: "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "dddddd",
|
|
||||||
name: "aaaa",
|
|
||||||
type: "text",
|
|
||||||
system: false,
|
|
||||||
required: false,
|
|
||||||
unique: false,
|
|
||||||
options: {
|
|
||||||
min: null,
|
|
||||||
max: null,
|
|
||||||
pattern: "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "squmamtm",
|
|
||||||
name: "test",
|
|
||||||
type: "date",
|
|
||||||
system: false,
|
|
||||||
required: false,
|
|
||||||
unique: false,
|
|
||||||
options: {
|
|
||||||
min: "",
|
|
||||||
max: "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const text2 = [
|
|
||||||
{
|
|
||||||
id: "zwWlxR46txtoAwx",
|
|
||||||
created: "2022-08-01 17:32:24.329",
|
|
||||||
updated: "2022-08-04 10:19:57.248",
|
|
||||||
name: "Demo",
|
|
||||||
system: true,
|
|
||||||
listRule: "userId = @request.user.id",
|
|
||||||
viewRule: "userId = @request.user.id",
|
|
||||||
createRule: "userId = @request.user.id",
|
|
||||||
updateRule: "userId = @request.user.id",
|
|
||||||
deleteRule: null,
|
|
||||||
schema: [
|
|
||||||
{
|
|
||||||
id: "nsght7oy",
|
|
||||||
name: "userId",
|
|
||||||
type: "user",
|
|
||||||
system: true,
|
|
||||||
required: true,
|
|
||||||
unique: true,
|
|
||||||
options: {
|
|
||||||
maxSelect: 1,
|
|
||||||
cascadeDelete: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "atpc4yjm",
|
|
||||||
name: "name",
|
|
||||||
type: "text",
|
|
||||||
system: false,
|
|
||||||
required: false,
|
|
||||||
unique: false,
|
|
||||||
options: {
|
|
||||||
min: null,
|
|
||||||
max: null,
|
|
||||||
pattern: "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "akb4s9de",
|
|
||||||
name: "avatar",
|
|
||||||
type: "file",
|
|
||||||
system: false,
|
|
||||||
required: false,
|
|
||||||
unique: true,
|
|
||||||
options: {
|
|
||||||
maxSelect: 1,
|
|
||||||
maxSize: 5242880,
|
|
||||||
mimeTypes: ["image/jpg", "image/jpeg", "image/png", "image/svg+xml", "image/gif"],
|
|
||||||
thumbs: null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "IV8FbE78jmXF56d",
|
|
||||||
created: "",
|
|
||||||
updated: "2022-08-04 10:21:54.100",
|
|
||||||
name: "abc",
|
|
||||||
system: false,
|
|
||||||
listRule: null,
|
|
||||||
viewRule: null,
|
|
||||||
createRule: null,
|
|
||||||
updateRule: null,
|
|
||||||
deleteRule: null,
|
|
||||||
schema: [
|
|
||||||
{
|
|
||||||
id: "t2pukeas",
|
|
||||||
name: "demo",
|
|
||||||
type: "text",
|
|
||||||
system: false,
|
|
||||||
required: false,
|
|
||||||
unique: false,
|
|
||||||
options: {
|
|
||||||
min: null,
|
|
||||||
max: null,
|
|
||||||
pattern: "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "dddddd",
|
|
||||||
name: "aaaa",
|
|
||||||
type: "text",
|
|
||||||
system: false,
|
|
||||||
required: false,
|
|
||||||
unique: false,
|
|
||||||
options: {
|
|
||||||
min: null,
|
|
||||||
max: null,
|
|
||||||
pattern: "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "squmamtm",
|
|
||||||
name: "test",
|
|
||||||
type: "date",
|
|
||||||
system: false,
|
|
||||||
required: false,
|
|
||||||
unique: false,
|
|
||||||
options: {
|
|
||||||
min: "",
|
|
||||||
max: "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "GGACt8sa1tcJp7T",
|
|
||||||
created: "2022-08-04 10:22:15.871",
|
|
||||||
updated: "2022-08-04 10:22:15.871",
|
|
||||||
name: "asdasd",
|
|
||||||
system: true,
|
|
||||||
listRule: null,
|
|
||||||
viewRule: null,
|
|
||||||
createRule: null,
|
|
||||||
updateRule: null,
|
|
||||||
deleteRule: null,
|
|
||||||
schema: [
|
|
||||||
{
|
|
||||||
id: "0eklwfvl",
|
|
||||||
name: "field",
|
|
||||||
type: "text",
|
|
||||||
system: false,
|
|
||||||
required: false,
|
|
||||||
unique: false,
|
|
||||||
options: {
|
|
||||||
min: null,
|
|
||||||
max: null,
|
|
||||||
pattern: "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// var diffs = dmp.diff_main(JSON.stringify(text1, null, 2), JSON.stringify(text2, null, 2));
|
|
||||||
|
|
||||||
var a = dmp.diff_linesToChars_(JSON.stringify(text1, null, 2), JSON.stringify(text2, null, 2));
|
|
||||||
var lineText1 = a.chars1;
|
|
||||||
var lineText2 = a.chars2;
|
|
||||||
var lineArray = a.lineArray;
|
|
||||||
var diffs = dmp.diff_main(lineText1, lineText2, false);
|
|
||||||
dmp.diff_charsToLines_(diffs, lineArray);
|
|
||||||
|
|
||||||
oldSchema = diff_prettyHtml(diffs, false);
|
|
||||||
newSchema = diff_prettyHtml(diffs, true);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<br />
|
|
||||||
<div class="grid">
|
|
||||||
<div class="col-6">
|
|
||||||
<code>
|
|
||||||
{@html oldSchema}
|
|
||||||
</code>
|
|
||||||
</div>
|
|
||||||
<div class="col-6">
|
|
||||||
<code>
|
|
||||||
{@html newSchema}
|
|
||||||
</code>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.collections-list {
|
|
||||||
column-count: 2;
|
|
||||||
column-gap: var(--baseSpacing);
|
|
||||||
}
|
|
||||||
code {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
overflow: auto;
|
|
||||||
padding: var(--xsSpacing);
|
|
||||||
white-space: pre;
|
|
||||||
background: var(--baseAlt1Color);
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,29 +0,0 @@
|
||||||
<script>
|
|
||||||
import OverlayPanel from "@/components/base/OverlayPanel.svelte";
|
|
||||||
import CollectionsExportForm from "@/components/collections/CollectionsExportForm.svelte";
|
|
||||||
|
|
||||||
let overlayPanel;
|
|
||||||
|
|
||||||
export function show() {
|
|
||||||
return overlayPanel?.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function hide() {
|
|
||||||
return overlayPanel?.hide();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<OverlayPanel
|
|
||||||
bind:this={overlayPanel}
|
|
||||||
class="overlay-panel-xl collections-export-panel"
|
|
||||||
on:hide
|
|
||||||
on:show
|
|
||||||
popup
|
|
||||||
active
|
|
||||||
>
|
|
||||||
<svelte:fragment slot="header">
|
|
||||||
<h4>Export collections schema</h4>
|
|
||||||
</svelte:fragment>
|
|
||||||
|
|
||||||
<CollectionsExportForm />
|
|
||||||
</OverlayPanel>
|
|
|
@ -13,7 +13,6 @@
|
||||||
import CollectionsSidebar from "@/components/collections/CollectionsSidebar.svelte";
|
import CollectionsSidebar from "@/components/collections/CollectionsSidebar.svelte";
|
||||||
import CollectionUpsertPanel from "@/components/collections/CollectionUpsertPanel.svelte";
|
import CollectionUpsertPanel from "@/components/collections/CollectionUpsertPanel.svelte";
|
||||||
import CollectionDocsPanel from "@/components/collections/docs/CollectionDocsPanel.svelte";
|
import CollectionDocsPanel from "@/components/collections/docs/CollectionDocsPanel.svelte";
|
||||||
import CollectionsExportPanel from "@/components/collections/CollectionsExportPanel.svelte";
|
|
||||||
import RecordUpsertPanel from "@/components/records/RecordUpsertPanel.svelte";
|
import RecordUpsertPanel from "@/components/records/RecordUpsertPanel.svelte";
|
||||||
import RecordsList from "@/components/records/RecordsList.svelte";
|
import RecordsList from "@/components/records/RecordsList.svelte";
|
||||||
|
|
||||||
|
@ -21,7 +20,6 @@
|
||||||
|
|
||||||
const queryParams = new URLSearchParams($querystring);
|
const queryParams = new URLSearchParams($querystring);
|
||||||
|
|
||||||
let collectionsExportPanel;
|
|
||||||
let collectionUpsertPanel;
|
let collectionUpsertPanel;
|
||||||
let collectionDocsPanel;
|
let collectionDocsPanel;
|
||||||
let recordPanel;
|
let recordPanel;
|
||||||
|
@ -132,8 +130,6 @@
|
||||||
</main>
|
</main>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<CollectionsExportPanel bind:this={collectionsExportPanel} />
|
|
||||||
|
|
||||||
<CollectionUpsertPanel bind:this={collectionUpsertPanel} />
|
<CollectionUpsertPanel bind:this={collectionUpsertPanel} />
|
||||||
|
|
||||||
<CollectionDocsPanel bind:this={collectionDocsPanel} />
|
<CollectionDocsPanel bind:this={collectionDocsPanel} />
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
<script>
|
<script>
|
||||||
|
import { createEventDispatcher } from "svelte";
|
||||||
|
import ApiClient from "@/utils/ApiClient";
|
||||||
import OverlayPanel from "@/components/base/OverlayPanel.svelte";
|
import OverlayPanel from "@/components/base/OverlayPanel.svelte";
|
||||||
|
import { addSuccessToast } from "@/stores/toasts";
|
||||||
|
|
||||||
export let title = "Side-by-side diff";
|
const dispatch = createEventDispatcher();
|
||||||
export let contentATitle = "Old state";
|
|
||||||
export let contentBTitle = "New state";
|
|
||||||
|
|
||||||
let panel;
|
let panel;
|
||||||
let contentA = "";
|
let oldCollections = [];
|
||||||
let contentB = "";
|
let newCollections = [];
|
||||||
|
let isImporting = false;
|
||||||
|
|
||||||
export function show(a, b) {
|
export function show(a, b) {
|
||||||
contentA = a;
|
oldCollections = a;
|
||||||
contentB = b;
|
newCollections = b;
|
||||||
|
|
||||||
panel?.show();
|
panel?.show();
|
||||||
}
|
}
|
||||||
|
@ -20,7 +22,7 @@
|
||||||
return panel?.hide();
|
return panel?.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
function diffsToHtml(diffs, ops = [DIFF_INSERT, DIFF_DELETE, DIFF_EQUAL]) {
|
function diffsToHtml(diffs, ops = [window.DIFF_INSERT, window.DIFF_DELETE, window.DIFF_EQUAL]) {
|
||||||
const html = [];
|
const html = [];
|
||||||
const pattern_amp = /&/g;
|
const pattern_amp = /&/g;
|
||||||
const pattern_lt = /</g;
|
const pattern_lt = /</g;
|
||||||
|
@ -56,35 +58,66 @@
|
||||||
return html.join("");
|
return html.join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
function diff(ops = [DIFF_INSERT, DIFF_DELETE, DIFF_EQUAL]) {
|
function diff(ops = [window.DIFF_INSERT, window.DIFF_DELETE, window.DIFF_EQUAL]) {
|
||||||
const dmp = new diff_match_patch();
|
const dmp = new diff_match_patch();
|
||||||
const lines = dmp.diff_linesToChars_(contentA, contentB);
|
const lines = dmp.diff_linesToChars_(
|
||||||
|
JSON.stringify(oldCollections, null, 4),
|
||||||
|
JSON.stringify(newCollections, null, 4)
|
||||||
|
);
|
||||||
const diffs = dmp.diff_main(lines.chars1, lines.chars2, false);
|
const diffs = dmp.diff_main(lines.chars1, lines.chars2, false);
|
||||||
|
|
||||||
dmp.diff_charsToLines_(diffs, lines.lineArray);
|
dmp.diff_charsToLines_(diffs, lines.lineArray);
|
||||||
|
|
||||||
return diffsToHtml(diffs, ops);
|
return diffsToHtml(diffs, ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function submitImport() {
|
||||||
|
if (isImporting) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isImporting = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await ApiClient.collections.import(newCollections);
|
||||||
|
addSuccessToast("Successfully imported the provided schema.");
|
||||||
|
dispatch("submit");
|
||||||
|
} catch (err) {
|
||||||
|
ApiClient.errorResponseHandler(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
hide();
|
||||||
|
|
||||||
|
isImporting = false;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<OverlayPanel bind:this={panel} class="full-width-popup diff-popup" popup on:show on:hide>
|
<OverlayPanel bind:this={panel} class="full-width-popup import-popup" popup on:show on:hide>
|
||||||
<svelte:fragment slot="header">
|
<svelte:fragment slot="header">
|
||||||
<h4 class="center txt-break">{title}</h4>
|
<h4 class="center txt-break">Side-by-side diff</h4>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
|
|
||||||
<div class="grid m-b-base">
|
<div class="grid m-b-base">
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<div class="section-title">{contentATitle}</div>
|
<div class="section-title">Old schema</div>
|
||||||
<code class="code-block">{@html diff([DIFF_DELETE, DIFF_EQUAL])}</code>
|
<code class="code-block">{@html diff([window.DIFF_DELETE, window.DIFF_EQUAL])}</code>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<div class="section-title">{contentBTitle}</div>
|
<div class="section-title">New schema</div>
|
||||||
<code class="code-block">{@html diff([DIFF_INSERT, DIFF_EQUAL])}</code>
|
<code class="code-block">{@html diff([window.DIFF_INSERT, window.DIFF_EQUAL])}</code>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<svelte:fragment slot="footer">
|
<svelte:fragment slot="footer">
|
||||||
<button type="button" class="btn btn-secondary" on:click={hide}>Close</button>
|
<button type="button" class="btn btn-secondary" on:click={hide}>Close</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-expanded m-l-auto"
|
||||||
|
class:btn-loading={isImporting}
|
||||||
|
on:click={() => submitImport()}
|
||||||
|
>
|
||||||
|
<span class="txt">Confirm and import</span>
|
||||||
|
</button>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</OverlayPanel>
|
</OverlayPanel>
|
||||||
|
|
|
@ -3,18 +3,18 @@
|
||||||
import ApiClient from "@/utils/ApiClient";
|
import ApiClient from "@/utils/ApiClient";
|
||||||
import CommonHelper from "@/utils/CommonHelper";
|
import CommonHelper from "@/utils/CommonHelper";
|
||||||
import { pageTitle } from "@/stores/app";
|
import { pageTitle } from "@/stores/app";
|
||||||
import { addInfoToast, addErrorToast } from "@/stores/toasts";
|
import { addErrorToast } from "@/stores/toasts";
|
||||||
|
import { setErrors } from "@/stores/errors";
|
||||||
import Field from "@/components/base/Field.svelte";
|
import Field from "@/components/base/Field.svelte";
|
||||||
import DiffPopup from "@/components/base/DiffPopup.svelte";
|
|
||||||
import SettingsSidebar from "@/components/settings/SettingsSidebar.svelte";
|
import SettingsSidebar from "@/components/settings/SettingsSidebar.svelte";
|
||||||
|
import ImportPopup from "@/components/settings/ImportPopup.svelte";
|
||||||
|
|
||||||
$pageTitle = "Import collections";
|
$pageTitle = "Import collections";
|
||||||
|
|
||||||
let fileInput;
|
let fileInput;
|
||||||
let diffPopup;
|
let importPopup;
|
||||||
|
|
||||||
let schema = "";
|
let schema = "";
|
||||||
let isImporting = false;
|
|
||||||
let isLoadingFile = false;
|
let isLoadingFile = false;
|
||||||
let newCollections = [];
|
let newCollections = [];
|
||||||
let oldCollections = [];
|
let oldCollections = [];
|
||||||
|
@ -141,22 +141,10 @@
|
||||||
reader.readAsText(file);
|
reader.readAsText(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
function submitImport() {
|
|
||||||
isImporting = true;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const newCollections = JSON.parse(schema);
|
|
||||||
ApiClient.collections.import(newCollections);
|
|
||||||
} catch (err) {
|
|
||||||
ApiClient.errorResponseHandler(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
isImporting = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function clear() {
|
function clear() {
|
||||||
schema = "";
|
schema = "";
|
||||||
fileInput.value = "";
|
fileInput.value = "";
|
||||||
|
setErrors({});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -224,36 +212,23 @@
|
||||||
<i class="ri-information-line" />
|
<i class="ri-information-line" />
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<string>Everything is up-to-date!</string>
|
<string>Your collections schema is already up-to-date!</string>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if isValid && newCollections.length && hasChanges}
|
{#if isValid && newCollections.length && hasChanges}
|
||||||
<div class="flex flex-gap-10">
|
<h5 class="section-title">Detected changes</h5>
|
||||||
<div>
|
|
||||||
<h5 class="section-title m-0">Detected changes</h5>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="btn btn-sm btn-warning"
|
|
||||||
on:click={() => {
|
|
||||||
diffPopup?.show(
|
|
||||||
JSON.stringify(oldCollections, null, 2),
|
|
||||||
JSON.stringify(newCollections, null, 2)
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
View diff
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="list m-t-sm">
|
<div class="list">
|
||||||
{#if collectionsToDelete.length}
|
{#if collectionsToDelete.length}
|
||||||
{#each collectionsToDelete as collection (collection.id)}
|
{#each collectionsToDelete as collection (collection.id)}
|
||||||
<div class="list-item">
|
<div class="list-item">
|
||||||
<span class="label label-danger list-label">Deleted</span>
|
<span class="label label-danger list-label">Deleted</span>
|
||||||
<strong>{collection.name}</strong>
|
<strong>{collection.name}</strong>
|
||||||
|
{#if collection.id}
|
||||||
|
<small class="txt-hint">({collection.id})</small>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -268,6 +243,9 @@
|
||||||
{/if}
|
{/if}
|
||||||
{entry.new.name}
|
{entry.new.name}
|
||||||
</strong>
|
</strong>
|
||||||
|
{#if entry.new.id}
|
||||||
|
<small class="txt-hint">({entry.new.id})</small>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -277,6 +255,9 @@
|
||||||
<div class="list-item">
|
<div class="list-item">
|
||||||
<span class="label label-success list-label">New</span>
|
<span class="label label-success list-label">New</span>
|
||||||
<strong>{collection.name}</strong>
|
<strong>{collection.name}</strong>
|
||||||
|
{#if collection.id}
|
||||||
|
<small class="txt-hint">({collection.id})</small>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -284,23 +265,19 @@
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="flex m-t-base">
|
<div class="flex m-t-base">
|
||||||
<button
|
{#if !!schema}
|
||||||
type="button"
|
<button type="button" class="btn btn-secondary link-hint" on:click={() => clear()}>
|
||||||
class="btn btn-secondary link-hint"
|
<span class="txt">Clear</span>
|
||||||
disabled={!schema || isImporting}
|
</button>
|
||||||
on:click={() => clear()}
|
{/if}
|
||||||
>
|
|
||||||
<span class="txt">Clear</span>
|
|
||||||
</button>
|
|
||||||
<div class="flex-fill" />
|
<div class="flex-fill" />
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-expanded m-l-auto"
|
class="btn btn-expanded btn-warning m-l-auto"
|
||||||
class:btn-loading={isImporting}
|
|
||||||
disabled={!canImport}
|
disabled={!canImport}
|
||||||
on:click={() => submitImport()}
|
on:click={() => importPopup?.show(oldCollections, newCollections)}
|
||||||
>
|
>
|
||||||
<span class="txt">Import</span>
|
<span class="txt">Review</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -308,7 +285,7 @@
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<DiffPopup bind:this={diffPopup} />
|
<ImportPopup bind:this={importPopup} on:submit={() => clear()} />
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.list-label {
|
.list-label {
|
||||||
|
|
|
@ -667,7 +667,7 @@ a,
|
||||||
border: 1px solid var(--baseAlt1Color);
|
border: 1px solid var(--baseAlt1Color);
|
||||||
border-radius: var(--baseRadius);
|
border-radius: var(--baseRadius);
|
||||||
.list-item {
|
.list-item {
|
||||||
word-break: break-all;
|
word-break: break-word;
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
@ -495,6 +495,7 @@ select {
|
||||||
font-size: var(--smFontSize);
|
font-size: var(--smFontSize);
|
||||||
line-height: var(--smLineHeight);
|
line-height: var(--smLineHeight);
|
||||||
color: var(--txtHintColor);
|
color: var(--txtHintColor);
|
||||||
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
.help-block-error {
|
.help-block-error {
|
||||||
color: var(--dangerColor);
|
color: var(--dangerColor);
|
||||||
|
|
Loading…
Reference in New Issue