import scaffoldings
This commit is contained in:
@@ -4,6 +4,9 @@
|
||||
import "prismjs/components/prism-dart.js";
|
||||
import "@/scss/prism_light.scss";
|
||||
|
||||
let classes = "";
|
||||
export { classes as class }; // export reserved keyword
|
||||
|
||||
export let content = "";
|
||||
export let language = "javascript"; // javascript, html
|
||||
|
||||
@@ -28,7 +31,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="code-wrapper prism-light">
|
||||
<div class="code-wrapper prism-light {classes}">
|
||||
<code>{@html formattedContent}</code>
|
||||
</div>
|
||||
|
||||
@@ -43,6 +46,9 @@
|
||||
.code-wrapper {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-height: 100%;
|
||||
overflow: auto; /* fallback */
|
||||
overflow: overlay;
|
||||
}
|
||||
.prism-light code {
|
||||
color: var(--txtPrimaryColor);
|
||||
|
||||
@@ -0,0 +1,352 @@
|
||||
<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>
|
||||
@@ -0,0 +1,29 @@
|
||||
<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,6 +13,7 @@
|
||||
import CollectionsSidebar from "@/components/collections/CollectionsSidebar.svelte";
|
||||
import CollectionUpsertPanel from "@/components/collections/CollectionUpsertPanel.svelte";
|
||||
import CollectionDocsPanel from "@/components/collections/docs/CollectionDocsPanel.svelte";
|
||||
import CollectionsExportPanel from "@/components/collections/CollectionsExportPanel.svelte";
|
||||
import RecordUpsertPanel from "@/components/records/RecordUpsertPanel.svelte";
|
||||
import RecordsList from "@/components/records/RecordsList.svelte";
|
||||
|
||||
@@ -20,6 +21,7 @@
|
||||
|
||||
const queryParams = new URLSearchParams($querystring);
|
||||
|
||||
let collectionsExportPanel;
|
||||
let collectionUpsertPanel;
|
||||
let collectionDocsPanel;
|
||||
let recordPanel;
|
||||
@@ -84,16 +86,18 @@
|
||||
<div class="breadcrumb-item">{$activeCollection.name}</div>
|
||||
</nav>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-secondary btn-circle"
|
||||
use:tooltip={{ text: "Edit collection", position: "right" }}
|
||||
on:click={() => collectionUpsertPanel?.show($activeCollection)}
|
||||
>
|
||||
<i class="ri-settings-4-line" />
|
||||
</button>
|
||||
<div class="inline-flex gap-5">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-secondary btn-circle"
|
||||
use:tooltip={{ text: "Edit collection", position: "right" }}
|
||||
on:click={() => collectionUpsertPanel?.show($activeCollection)}
|
||||
>
|
||||
<i class="ri-settings-4-line" />
|
||||
</button>
|
||||
|
||||
<RefreshButton on:refresh={() => recordsList?.load()} />
|
||||
<RefreshButton on:refresh={() => recordsList?.load()} />
|
||||
</div>
|
||||
|
||||
<div class="btns-group">
|
||||
<button
|
||||
@@ -128,6 +132,8 @@
|
||||
</main>
|
||||
{/if}
|
||||
|
||||
<CollectionsExportPanel bind:this={collectionsExportPanel} />
|
||||
|
||||
<CollectionUpsertPanel bind:this={collectionUpsertPanel} />
|
||||
|
||||
<CollectionDocsPanel bind:this={collectionDocsPanel} />
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
<script>
|
||||
import ApiClient from "@/utils/ApiClient";
|
||||
import CommonHelper from "@/utils/CommonHelper";
|
||||
import { pageTitle } from "@/stores/app";
|
||||
import { addInfoToast } from "@/stores/toasts";
|
||||
import CodeBlock from "@/components/base/CodeBlock.svelte";
|
||||
import SettingsSidebar from "@/components/settings/SettingsSidebar.svelte";
|
||||
|
||||
$pageTitle = "Export collections";
|
||||
|
||||
const uniqueId = "export_" + CommonHelper.randomString(5);
|
||||
|
||||
let collections = [];
|
||||
let isLoadingCollections = false;
|
||||
|
||||
$: schema = JSON.stringify(collections, null, 2);
|
||||
|
||||
loadCollections();
|
||||
|
||||
async function loadCollections() {
|
||||
isLoadingCollections = true;
|
||||
|
||||
try {
|
||||
collections = await ApiClient.collections.getFullList(100, {
|
||||
$cancelKey: uniqueId,
|
||||
});
|
||||
// delete timestamps
|
||||
for (let collection of collections) {
|
||||
delete collection.created;
|
||||
delete collection.updated;
|
||||
}
|
||||
} catch (err) {
|
||||
ApiClient.errorResponseHandler(err);
|
||||
}
|
||||
|
||||
isLoadingCollections = false;
|
||||
}
|
||||
|
||||
function download() {
|
||||
CommonHelper.downloadJson(collections, "pb_schema");
|
||||
}
|
||||
|
||||
function copy() {
|
||||
CommonHelper.copyToClipboard(schema);
|
||||
addInfoToast("The schema was copied to your clipboard!", 3000);
|
||||
}
|
||||
</script>
|
||||
|
||||
<SettingsSidebar />
|
||||
|
||||
<main class="page-wrapper">
|
||||
<header class="page-header">
|
||||
<nav class="breadcrumbs">
|
||||
<div class="breadcrumb-item">Settings</div>
|
||||
<div class="breadcrumb-item">{$pageTitle}</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<div class="wrapper">
|
||||
<div class="panel">
|
||||
{#if isLoadingCollections}
|
||||
<div class="loader" />
|
||||
{:else}
|
||||
<div class="content txt-xl m-b-base">
|
||||
<p>
|
||||
Below you'll find your current collections schema that you could import later in
|
||||
another PocketBase environment.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="export-preview">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-secondary fade copy-schema"
|
||||
on:click={() => copy()}
|
||||
>
|
||||
<span class="txt">Copy</span>
|
||||
</button>
|
||||
|
||||
<CodeBlock content={schema} />
|
||||
</div>
|
||||
|
||||
<div class="flex m-t-base">
|
||||
<div class="flex-fill" />
|
||||
<button type="button" class="btn btn-expanded" on:click={() => download()}>
|
||||
<i class="ri-download-line" />
|
||||
<span class="txt">Download as JSON</span>
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
.export-preview {
|
||||
position: relative;
|
||||
height: 500px;
|
||||
}
|
||||
.export-preview .copy-schema {
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
top: 15px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,221 @@
|
||||
<script>
|
||||
import ApiClient from "@/utils/ApiClient";
|
||||
import CommonHelper from "@/utils/CommonHelper";
|
||||
import { pageTitle } from "@/stores/app";
|
||||
import { addInfoToast, addErrorToast } from "@/stores/toasts";
|
||||
import Field from "@/components/base/Field.svelte";
|
||||
import CodeBlock from "@/components/base/CodeBlock.svelte";
|
||||
import SettingsSidebar from "@/components/settings/SettingsSidebar.svelte";
|
||||
|
||||
$pageTitle = "Import collections";
|
||||
|
||||
let uniquePageId = "import_" + CommonHelper.randomString(5);
|
||||
|
||||
let fileInput;
|
||||
|
||||
let schema = "";
|
||||
let isImporting = false;
|
||||
let isLoadingFile = false;
|
||||
let newCollections = [];
|
||||
let oldCollections = [];
|
||||
let isLoadingOldCollections = false;
|
||||
|
||||
$: if (typeof schema !== "undefined") {
|
||||
loadNewCollections(schema);
|
||||
}
|
||||
|
||||
$: isValid =
|
||||
!!schema &&
|
||||
newCollections.length &&
|
||||
newCollections.length === newCollections.filter((item) => !!item.id && !!item.name).length;
|
||||
|
||||
$: canImport = isValid && !isLoadingOldCollections;
|
||||
|
||||
$: collectionsToDelete = oldCollections.filter((collection) => {
|
||||
return !CommonHelper.findByKey(newCollections, "id", collection.id);
|
||||
});
|
||||
|
||||
$: collectionsToAdd = newCollections.filter((collection) => {
|
||||
return !CommonHelper.findByKey(oldCollections, "id", collection.id);
|
||||
});
|
||||
|
||||
$: collectionsToModify = newCollections.filter((newCollection) => {
|
||||
const oldCollection = CommonHelper.findByKey(oldCollections, "id", newCollection.id);
|
||||
if (!oldCollection?.id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return JSON.stringify(oldCollection) !== JSON.stringify(newCollection);
|
||||
});
|
||||
|
||||
loadOldCollections();
|
||||
|
||||
async function loadOldCollections() {
|
||||
isLoadingOldCollections = true;
|
||||
|
||||
try {
|
||||
oldCollections = await ApiClient.collections.getFullList(100, {
|
||||
$cancelKey: uniquePageId,
|
||||
});
|
||||
// delete timestamps
|
||||
for (let collection of oldCollections) {
|
||||
delete collection.created;
|
||||
delete collection.updated;
|
||||
}
|
||||
} catch (err) {
|
||||
ApiClient.errorResponseHandler(err);
|
||||
}
|
||||
|
||||
isLoadingOldCollections = false;
|
||||
}
|
||||
|
||||
function loadNewCollections() {
|
||||
newCollections = [];
|
||||
|
||||
try {
|
||||
newCollections = JSON.parse(schema);
|
||||
} catch (_) {}
|
||||
|
||||
if (!Array.isArray(newCollections)) {
|
||||
newCollections = [];
|
||||
}
|
||||
|
||||
// delete timestamps
|
||||
for (let collection of newCollections) {
|
||||
delete collection.created;
|
||||
delete collection.updated;
|
||||
}
|
||||
}
|
||||
|
||||
function loadFile(file) {
|
||||
isLoadingFile = true;
|
||||
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = (event) => {
|
||||
schema = event.target.result;
|
||||
|
||||
isLoadingFile = false;
|
||||
fileInput.value = ""; // reset
|
||||
};
|
||||
|
||||
reader.onerror = (err) => {
|
||||
console.log(err);
|
||||
addErrorToast("Failed to load the imported JSON.");
|
||||
|
||||
isLoadingFile = false;
|
||||
fileInput.value = ""; // reset
|
||||
};
|
||||
|
||||
reader.readAsText(file);
|
||||
}
|
||||
|
||||
function submitImport() {
|
||||
isImporting = true;
|
||||
|
||||
try {
|
||||
const newCollections = JSON.parse(schema);
|
||||
ApiClient.collections.import(newCollections);
|
||||
} catch (err) {
|
||||
ApiClient.errorResponseHandler(err);
|
||||
}
|
||||
|
||||
isImporting = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<SettingsSidebar />
|
||||
|
||||
<main class="page-wrapper">
|
||||
<header class="page-header">
|
||||
<nav class="breadcrumbs">
|
||||
<div class="breadcrumb-item">Settings</div>
|
||||
<div class="breadcrumb-item">{$pageTitle}</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<div class="wrapper">
|
||||
<div class="panel">
|
||||
<div class="content txt-xl m-b-base">
|
||||
<input
|
||||
bind:this={fileInput}
|
||||
type="file"
|
||||
class="hidden"
|
||||
accept=".json"
|
||||
on:change={() => {
|
||||
if (fileInput.files.length) {
|
||||
loadFile(fileInput.files[0]);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<p>
|
||||
Paste below the collections schema you want to import or
|
||||
<button
|
||||
class="btn btn-outline btn-sm"
|
||||
class:btn-loading={isLoadingFile}
|
||||
on:click={() => {
|
||||
fileInput.click();
|
||||
}}
|
||||
>
|
||||
<span class="txt">Import from JSON file</span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Field class="form-field {!isValid ? 'field-error' : ''}" name="collections" let:uniqueId>
|
||||
<label for={uniqueId}>Collections schema</label>
|
||||
<textarea
|
||||
id={uniqueId}
|
||||
class="json-editor"
|
||||
spellcheck="false"
|
||||
rows="20"
|
||||
required
|
||||
bind:value={schema}
|
||||
/>
|
||||
{#if !!schema && !isValid}
|
||||
<div class="help-block help-block-error">Invalid collections schema.</div>
|
||||
{/if}
|
||||
</Field>
|
||||
|
||||
<div class="section-title">Detected changes</div>
|
||||
<p>No changes to your current collections schema were found.</p>
|
||||
|
||||
{#each collectionsToDelete as collection (collection.id)}
|
||||
Delete {collection.name}
|
||||
<br />
|
||||
{/each}
|
||||
|
||||
{#each collectionsToModify as collection (collection.id)}
|
||||
Modify {collection.name}
|
||||
<br />
|
||||
{/each}
|
||||
|
||||
{#each collectionsToAdd as collection (collection.id)}
|
||||
Add {collection.name}
|
||||
<br />
|
||||
{/each}
|
||||
|
||||
<div class="flex m-t-base">
|
||||
<div class="flex-fill" />
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-expanded"
|
||||
class:btn-loading={isImporting}
|
||||
disabled={!canImport}
|
||||
on:click={() => submitImport()}
|
||||
>
|
||||
<span class="txt">Import</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
.json-editor {
|
||||
font-size: 15px;
|
||||
line-height: 1.379rem;
|
||||
font-family: var(--monospaceFontFamily);
|
||||
}
|
||||
</style>
|
||||
@@ -29,6 +29,26 @@
|
||||
<span class="txt">Files storage</span>
|
||||
</a>
|
||||
|
||||
<div class="sidebar-title">Sync</div>
|
||||
<a
|
||||
href="/settings/export-collections"
|
||||
class="sidebar-list-item"
|
||||
use:active={{ path: "/settings/export-collections/?.*" }}
|
||||
use:link
|
||||
>
|
||||
<i class="ri-uninstall-line" />
|
||||
<span class="txt">Export collections</span>
|
||||
</a>
|
||||
<a
|
||||
href="/settings/import-collections"
|
||||
class="sidebar-list-item"
|
||||
use:active={{ path: "/settings/import-collections/?.*" }}
|
||||
use:link
|
||||
>
|
||||
<i class="ri-install-line" />
|
||||
<span class="txt">Import collections</span>
|
||||
</a>
|
||||
|
||||
<div class="sidebar-title">Authentication</div>
|
||||
<a
|
||||
href="/settings/auth-providers"
|
||||
|
||||
@@ -55,10 +55,11 @@
|
||||
clearList();
|
||||
}
|
||||
|
||||
return ApiClient.users.getList(page, 50, {
|
||||
sort: sort || "-created",
|
||||
filter: filter,
|
||||
})
|
||||
return ApiClient.users
|
||||
.getList(page, 50, {
|
||||
sort: sort || "-created",
|
||||
filter: filter,
|
||||
})
|
||||
.then((result) => {
|
||||
isLoadingUsers = false;
|
||||
users = users.concat(result.items);
|
||||
|
||||
Reference in New Issue
Block a user