updated CollectionsImport and CollectionUpsert forms
This commit is contained in:
@@ -14,7 +14,6 @@
|
||||
"devDependencies": {
|
||||
"@codemirror/autocomplete": "^6.0.0",
|
||||
"@codemirror/commands": "^6.0.0",
|
||||
"@codemirror/lang-javascript": "^6.0.2",
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/legacy-modes": "^6.0.0",
|
||||
"@codemirror/search": "^6.0.0",
|
||||
|
||||
@@ -101,7 +101,8 @@
|
||||
}
|
||||
|
||||
confirm(`Do you really want to delete the selected admin?`, () => {
|
||||
return ApiClient.admins.delete(admin.id)
|
||||
return ApiClient.admins
|
||||
.delete(admin.id)
|
||||
.then(() => {
|
||||
confirmClose = false;
|
||||
hide();
|
||||
@@ -190,7 +191,7 @@
|
||||
|
||||
{#if admin.isNew || changePasswordToggle}
|
||||
<div class="col-12">
|
||||
<div class="grid" transition:slide={{ duration: 150 }}>
|
||||
<div class="grid" transition:slide|local={{ duration: 150 }}>
|
||||
<div class="col-sm-6">
|
||||
<Field class="form-field required" name="password" let:uniqueId>
|
||||
<label for={uniqueId}>
|
||||
|
||||
@@ -36,9 +36,10 @@
|
||||
export async function load() {
|
||||
isLoading = true;
|
||||
|
||||
return ApiClient.logs.getRequestsStats({
|
||||
filter: [presets, filter].filter(Boolean).join("&&"),
|
||||
})
|
||||
return ApiClient.logs
|
||||
.getRequestsStats({
|
||||
filter: [presets, filter].filter(Boolean).join("&&"),
|
||||
})
|
||||
.then((result) => {
|
||||
resetData();
|
||||
for (let item of result) {
|
||||
@@ -147,7 +148,7 @@
|
||||
|
||||
<div class="chart-wrapper" class:loading={isLoading}>
|
||||
{#if isLoading}
|
||||
<div class="chart-loader loader" transition:scale={{ duration: 150 }} />
|
||||
<div class="chart-loader loader" transition:scale|local={{ duration: 150 }} />
|
||||
{/if}
|
||||
<canvas bind:this={chartCanvas} class="chart-canvas" style="height: 250px; width: 100%;" />
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script>
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import ApiClient from "@/utils/ApiClient";
|
||||
import CommonHelper from "@/utils/CommonHelper";
|
||||
import OverlayPanel from "@/components/base/OverlayPanel.svelte";
|
||||
import { addSuccessToast } from "@/stores/toasts";
|
||||
|
||||
@@ -9,8 +10,13 @@
|
||||
let panel;
|
||||
let oldCollections = [];
|
||||
let newCollections = [];
|
||||
let changes = [];
|
||||
let isImporting = false;
|
||||
|
||||
$: if (Array.isArray(oldCollections) && Array.isArray(newCollections)) {
|
||||
loadChanges();
|
||||
}
|
||||
|
||||
export function show(a, b) {
|
||||
oldCollections = a;
|
||||
newCollections = b;
|
||||
@@ -22,6 +28,32 @@
|
||||
return panel?.hide();
|
||||
}
|
||||
|
||||
function loadChanges() {
|
||||
changes = [];
|
||||
|
||||
// add deleted and modified collections
|
||||
for (const oldCollection of oldCollections) {
|
||||
const newCollection = CommonHelper.findByKey(newCollections, "id", oldCollection.id) || null;
|
||||
if (!newCollection?.id || JSON.stringify(oldCollection) != JSON.stringify(newCollection)) {
|
||||
changes.push({
|
||||
old: oldCollection,
|
||||
new: newCollection,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// add only new collections
|
||||
for (const newCollection of newCollections) {
|
||||
const oldCollection = CommonHelper.findByKey(oldCollections, "id", newCollection.id) || null;
|
||||
if (!oldCollection?.id) {
|
||||
changes.push({
|
||||
old: oldCollection,
|
||||
new: newCollection,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function diffsToHtml(diffs, ops = [window.DIFF_INSERT, window.DIFF_DELETE, window.DIFF_EQUAL]) {
|
||||
const html = [];
|
||||
const pattern_amp = /&/g;
|
||||
@@ -58,11 +90,11 @@
|
||||
return html.join("");
|
||||
}
|
||||
|
||||
function diff(ops = [window.DIFF_INSERT, window.DIFF_DELETE, window.DIFF_EQUAL]) {
|
||||
function diff(obj1, obj2, ops = [window.DIFF_INSERT, window.DIFF_DELETE, window.DIFF_EQUAL]) {
|
||||
const dmp = new diff_match_patch();
|
||||
const lines = dmp.diff_linesToChars_(
|
||||
JSON.stringify(oldCollections, null, 4),
|
||||
JSON.stringify(newCollections, null, 4)
|
||||
obj1 ? JSON.stringify(obj1, null, 4) : "",
|
||||
obj2 ? JSON.stringify(obj2, null, 4) : ""
|
||||
);
|
||||
const diffs = dmp.diff_main(lines.chars1, lines.chars2, false);
|
||||
|
||||
@@ -80,7 +112,7 @@
|
||||
|
||||
try {
|
||||
await ApiClient.collections.import(newCollections);
|
||||
addSuccessToast("Successfully imported the provided schema.");
|
||||
addSuccessToast("Successfully imported the provided collections.");
|
||||
dispatch("submit");
|
||||
} catch (err) {
|
||||
ApiClient.errorResponseHandler(err);
|
||||
@@ -92,27 +124,58 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<OverlayPanel bind:this={panel} class="full-width-popup import-popup" popup on:show on:hide>
|
||||
<OverlayPanel
|
||||
bind:this={panel}
|
||||
class="full-width-popup import-popup"
|
||||
overlayClose={false}
|
||||
popup
|
||||
on:show
|
||||
on:hide
|
||||
>
|
||||
<svelte:fragment slot="header">
|
||||
<h4 class="center txt-break">Side-by-side diff</h4>
|
||||
</svelte:fragment>
|
||||
|
||||
<div class="grid m-b-base">
|
||||
<div class="col-6">
|
||||
<div class="section-title">Old schema</div>
|
||||
<code class="code-block">{@html diff([window.DIFF_DELETE, window.DIFF_EQUAL])}</code>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="section-title">New schema</div>
|
||||
<code class="code-block">{@html diff([window.DIFF_INSERT, window.DIFF_EQUAL])}</code>
|
||||
</div>
|
||||
<div class="grid grid-sm m-b-sm">
|
||||
{#each changes as pair (pair.old?.id + pair.new?.id)}
|
||||
<div class="col-12">
|
||||
<div class="flex flex-gap-10">
|
||||
{#if !pair.old?.id}
|
||||
<span class="label label-success">New</span>
|
||||
<strong>{pair.new?.name}</strong>
|
||||
{:else if !pair.new?.id}
|
||||
<span class="label label-danger">Deleted</span>
|
||||
<strong>{pair.old?.name}</strong>
|
||||
{:else}
|
||||
<span class="label label-warning">Modified</span>
|
||||
<div class="inline-flex fleg-gap-5">
|
||||
{#if pair.old.name !== pair.new.name}
|
||||
<strong class="txt-strikethrough txt-hint">{pair.old.name}</strong>
|
||||
<i class="ri-arrow-right-line txt-sm" />
|
||||
{/if}
|
||||
<strong class="txt">{pair.new.name}</strong>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 p-b-10">
|
||||
<code class="code-block">
|
||||
{@html diff(pair.old, pair.new, [window.DIFF_DELETE, window.DIFF_EQUAL])}
|
||||
</code>
|
||||
</div>
|
||||
<div class="col-6 p-b-10">
|
||||
<code class="code-block">
|
||||
{@html diff(pair.old, pair.new, [window.DIFF_INSERT, window.DIFF_EQUAL])}
|
||||
</code>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<button type="button" class="btn btn-secondary" on:click={hide}>Close</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-expanded m-l-auto"
|
||||
class="btn btn-expanded"
|
||||
class:btn-loading={isImporting}
|
||||
on:click={() => submitImport()}
|
||||
>
|
||||
|
||||
@@ -14,19 +14,19 @@
|
||||
let fileInput;
|
||||
let importPopup;
|
||||
|
||||
let schema = "";
|
||||
let schemas = "";
|
||||
let isLoadingFile = false;
|
||||
let newCollections = [];
|
||||
let oldCollections = [];
|
||||
let collectionsToModify = [];
|
||||
let isLoadingOldCollections = false;
|
||||
|
||||
$: if (typeof schema !== "undefined") {
|
||||
loadNewCollections(schema);
|
||||
$: if (typeof schemas !== "undefined") {
|
||||
loadNewCollections(schemas);
|
||||
}
|
||||
|
||||
$: isValid =
|
||||
!!schema &&
|
||||
!!schemas &&
|
||||
newCollections.length &&
|
||||
newCollections.length === newCollections.filter((item) => !!item.id && !!item.name).length;
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
}
|
||||
|
||||
$: hasChanges =
|
||||
!!schema && (collectionsToDelete.length || collectionsToAdd.length || collectionsToModify.length);
|
||||
!!schemas && (collectionsToDelete.length || collectionsToAdd.length || collectionsToModify.length);
|
||||
|
||||
$: canImport = !isLoadingOldCollections && isValid && hasChanges;
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
newCollections = [];
|
||||
|
||||
try {
|
||||
newCollections = JSON.parse(schema);
|
||||
newCollections = JSON.parse(schemas);
|
||||
} catch (_) {}
|
||||
|
||||
if (!Array.isArray(newCollections)) {
|
||||
@@ -120,12 +120,12 @@
|
||||
isLoadingFile = false;
|
||||
fileInput.value = ""; // reset
|
||||
|
||||
schema = event.target.result;
|
||||
schemas = event.target.result;
|
||||
|
||||
await tick();
|
||||
|
||||
if (!newCollections.length) {
|
||||
addErrorToast("Invalid collections schema.");
|
||||
addErrorToast("Invalid collections list.");
|
||||
clear();
|
||||
}
|
||||
};
|
||||
@@ -142,7 +142,7 @@
|
||||
}
|
||||
|
||||
function clear() {
|
||||
schema = "";
|
||||
schemas = "";
|
||||
fileInput.value = "";
|
||||
setErrors({});
|
||||
}
|
||||
@@ -177,7 +177,7 @@
|
||||
/>
|
||||
|
||||
<p>
|
||||
Paste below the collections schema you want to import or
|
||||
Paste below the collections you want to import or
|
||||
<button
|
||||
class="btn btn-outline btn-sm m-l-5"
|
||||
class:btn-loading={isLoadingFile}
|
||||
@@ -191,18 +191,18 @@
|
||||
</div>
|
||||
|
||||
<Field class="form-field {!isValid ? 'field-error' : ''}" name="collections" let:uniqueId>
|
||||
<label for={uniqueId} class="p-b-10">Collections schema</label>
|
||||
<label for={uniqueId} class="p-b-10">Collections</label>
|
||||
<textarea
|
||||
id={uniqueId}
|
||||
class="code"
|
||||
spellcheck="false"
|
||||
rows="15"
|
||||
required
|
||||
bind:value={schema}
|
||||
bind:value={schemas}
|
||||
/>
|
||||
|
||||
{#if !!schema && !isValid}
|
||||
<div class="help-block help-block-error">Invalid collections schema.</div>
|
||||
{#if !!schemas && !isValid}
|
||||
<div class="help-block help-block-error">Invalid collections schemas.</div>
|
||||
{/if}
|
||||
</Field>
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
<i class="ri-information-line" />
|
||||
</div>
|
||||
<div class="content">
|
||||
<string>Your collections schema is already up-to-date!</string>
|
||||
<string>Your collections structure is already up-to-date!</string>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -234,17 +234,17 @@
|
||||
{/if}
|
||||
|
||||
{#if collectionsToModify.length}
|
||||
{#each collectionsToModify as entry (entry.old.id + entry.new.id)}
|
||||
{#each collectionsToModify as pair (pair.old.id + pair.new.id)}
|
||||
<div class="list-item">
|
||||
<span class="label label-warning list-label">Modified</span>
|
||||
<strong>
|
||||
{#if entry.old.name !== entry.new.name}
|
||||
<span class="txt-strikethrough txt-hint">{entry.old.name}</span> -
|
||||
{#if pair.old.name !== pair.new.name}
|
||||
<span class="txt-strikethrough txt-hint">{pair.old.name}</span> -
|
||||
{/if}
|
||||
{entry.new.name}
|
||||
{pair.new.name}
|
||||
</strong>
|
||||
{#if entry.new.id}
|
||||
<small class="txt-hint">({entry.new.id})</small>
|
||||
{#if pair.new.id}
|
||||
<small class="txt-hint">({pair.new.id})</small>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
@@ -265,7 +265,7 @@
|
||||
{/if}
|
||||
|
||||
<div class="flex m-t-base">
|
||||
{#if !!schema}
|
||||
{#if !!schemas}
|
||||
<button type="button" class="btn btn-secondary link-hint" on:click={() => clear()}>
|
||||
<span class="txt">Clear</span>
|
||||
</button>
|
||||
|
||||
@@ -98,7 +98,8 @@
|
||||
}
|
||||
|
||||
confirm(`Do you really want to delete the selected user?`, () => {
|
||||
return ApiClient.users.delete(user.id)
|
||||
return ApiClient.users
|
||||
.delete(user.id)
|
||||
.then(() => {
|
||||
confirmClose = false;
|
||||
hide();
|
||||
@@ -112,7 +113,8 @@
|
||||
}
|
||||
|
||||
function sendVerificationEmail(notify = true) {
|
||||
return ApiClient.users.requestVerification(user.isNew ? email : user.email)
|
||||
return ApiClient.users
|
||||
.requestVerification(user.isNew ? email : user.email)
|
||||
.then(() => {
|
||||
confirmClose = false;
|
||||
hide();
|
||||
@@ -182,7 +184,7 @@
|
||||
|
||||
{#if user.isNew || changePasswordToggle}
|
||||
<div class="col-12">
|
||||
<div class="grid" transition:slide={{ duration: 150 }}>
|
||||
<div class="grid" transition:slide|local={{ duration: 150 }}>
|
||||
<div class="col-sm-6">
|
||||
<Field class="form-field required" name="password" let:uniqueId>
|
||||
<label for={uniqueId}>
|
||||
|
||||
Reference in New Issue
Block a user