removed lodash and axios
This commit is contained in:
@@ -83,3 +83,74 @@ export function apiGet(url, options = {}) {
|
|||||||
},
|
},
|
||||||
}).then((r) => r.json());
|
}).then((r) => r.json());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isEqual(db, ed) {
|
||||||
|
let isObject = (x) =>
|
||||||
|
typeof x === "object" && !Array.isArray(x) && x !== null;
|
||||||
|
let isArray = (x) => x?.constructor === Array;
|
||||||
|
let isEmpty = (x) => x === null || x === undefined || x == [];
|
||||||
|
const db_value = db ?? null;
|
||||||
|
const ed_value = ed ?? null;
|
||||||
|
|
||||||
|
if (isObject(db_value)) {
|
||||||
|
let keys = Object.keys(db_value);
|
||||||
|
return keys.reduce((acc, k) => {
|
||||||
|
if (acc === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return isEqual(db_value?.[k], ed_value?.[k]);
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
if (isArray(db_value)) {
|
||||||
|
return db_value.reduce((c, v, i) => {
|
||||||
|
if (c === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return isEqual(v, ed_value?.[i]);
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEmpty(db_value) && isEmpty(ed_value)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (db_value == ed_value) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// const ok = Object.keys,
|
||||||
|
// tx = typeof x,
|
||||||
|
// ty = typeof y;
|
||||||
|
// return x && y && tx === "object" && tx === ty
|
||||||
|
// ? ok(x).length === ok(y).length &&
|
||||||
|
// ok(x).every((key) => isEqual(x[key], y[key]))
|
||||||
|
// : x === y;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function debounce(fn, delay) {
|
||||||
|
let timer;
|
||||||
|
return (...args) => {
|
||||||
|
clearTimeout(timer);
|
||||||
|
timer = setTimeout(() => fn(...args), delay);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function arrayUnique(array) {
|
||||||
|
return array.filter((value, index) => array.indexOf(value) === index);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function arrayUniqueBy(items, uniqueBy) {
|
||||||
|
const ids = new Set(items.map((item) => item[uniqueBy]));
|
||||||
|
return [...ids].map((id) => items.find((i) => i[uniqueBy] === id));
|
||||||
|
}
|
||||||
|
export function arrayUniqueCb(items, aFilter) {
|
||||||
|
const cache = new Set();
|
||||||
|
return items.filter((item) => {
|
||||||
|
const cacheValue = aFilter(item);
|
||||||
|
if (cache.has(cacheValue)) return false;
|
||||||
|
cache.add(cacheValue);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,10 +25,8 @@
|
|||||||
// export let layout;
|
// export let layout;
|
||||||
export let channel;
|
export let channel;
|
||||||
|
|
||||||
export let axios;
|
|
||||||
export let readableSchemas;
|
export let readableSchemas;
|
||||||
|
|
||||||
setContext("axios", axios);
|
|
||||||
setContext("channel", channel);
|
setContext("channel", channel);
|
||||||
setContext(
|
setContext(
|
||||||
"readableSchemas",
|
"readableSchemas",
|
||||||
|
|||||||
@@ -2,8 +2,9 @@
|
|||||||
import ErrorAlert from "../common/ErrorAlert.svelte";
|
import ErrorAlert from "../common/ErrorAlert.svelte";
|
||||||
import SpinnerButton from "../common/SpinnerButton.svelte";
|
import SpinnerButton from "../common/SpinnerButton.svelte";
|
||||||
import Avatar from "./Avatar.svelte";
|
import Avatar from "./Avatar.svelte";
|
||||||
import {getContext} from "svelte";
|
import { getContext } from "svelte";
|
||||||
import SuccessAlert from "../common/SuccessAlert.svelte";
|
import SuccessAlert from "../common/SuccessAlert.svelte";
|
||||||
|
import { apiPost } from "../../helpers";
|
||||||
|
|
||||||
const user = getContext("user");
|
const user = getContext("user");
|
||||||
const channel = getContext("channel");
|
const channel = getContext("channel");
|
||||||
@@ -16,16 +17,15 @@
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
errorMessage = "";
|
errorMessage = "";
|
||||||
|
|
||||||
axios
|
apiPost(channel.lucentUrl + "/account/update-name", {
|
||||||
.post(channel.lucentUrl + "/account/update-name", {
|
name: name,
|
||||||
name: name,
|
})
|
||||||
})
|
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
successAlert.show();
|
successAlert.show();
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
errorMessage = error.response?.data.error;
|
errorMessage = error.response?.data.error;
|
||||||
console.log({errorMessage});
|
console.log({ errorMessage });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,55 +33,55 @@
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
errorMessage = "";
|
errorMessage = "";
|
||||||
|
|
||||||
axios
|
apiPost(channel.lucentUrl + "/account/update-email", {
|
||||||
.post(channel.lucentUrl + "/account/update-email", {
|
email: email,
|
||||||
email: email,
|
})
|
||||||
})
|
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
successAlert.show();
|
successAlert.show();
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
errorMessage = error.response?.data.error;
|
errorMessage = error.response?.data.error;
|
||||||
console.log({errorMessage});
|
console.log({ errorMessage });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<div class="wrapper-tiny">
|
<div class="wrapper-tiny">
|
||||||
<ErrorAlert message={errorMessage}/>
|
<ErrorAlert message={errorMessage} />
|
||||||
<SuccessAlert bind:this={successAlert} />
|
<SuccessAlert bind:this={successAlert} />
|
||||||
<h3 class="header-small mb-5">
|
<h3 class="header-small mb-5">
|
||||||
<Avatar name={user.name}/>
|
<Avatar name={user.name} />
|
||||||
</h3>
|
</h3>
|
||||||
<form on:submit={saveName}>
|
<form on:submit={saveName}>
|
||||||
<div class="input-group mb-5">
|
<div class="input-group mb-5">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
bind:value={name}
|
bind:value={name}
|
||||||
class="form-control mb-3"
|
class="form-control mb-3"
|
||||||
placeholder="Name"
|
placeholder="Name"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<SpinnerButton label="Update Name"/>
|
<SpinnerButton label="Update Name" />
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<form on:submit={saveEmail}>
|
<form on:submit={saveEmail}>
|
||||||
<div class="input-group mb-5">
|
<div class="input-group mb-5">
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
bind:value={email}
|
bind:value={email}
|
||||||
class="form-control mb-3"
|
class="form-control mb-3"
|
||||||
placeholder="Email"
|
placeholder="Email"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<SpinnerButton label="Update Email"/>
|
<SpinnerButton label="Update Email" />
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="list-group">
|
<div class="list-group">
|
||||||
<a class="list-group-item list-group-item-action" href="{ channel.lucentUrl }/logout">Logout from this
|
<a
|
||||||
device</a>
|
class="list-group-item list-group-item-action"
|
||||||
|
href="{channel.lucentUrl}/logout">Logout from this device</a
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
<script>
|
<script>
|
||||||
|
import { apiPost } from "../../helpers";
|
||||||
import ErrorAlert from "../common/ErrorAlert.svelte";
|
import ErrorAlert from "../common/ErrorAlert.svelte";
|
||||||
import SpinnerButton from "../common/SpinnerButton.svelte";
|
import SpinnerButton from "../common/SpinnerButton.svelte";
|
||||||
import {getContext} from "svelte";
|
import { getContext } from "svelte";
|
||||||
|
|
||||||
const channel = getContext("channel");
|
const channel = getContext("channel");
|
||||||
let name = "";
|
let name = "";
|
||||||
@@ -12,48 +13,45 @@
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
errorMessage = "";
|
errorMessage = "";
|
||||||
|
|
||||||
axios
|
apiPost(channel.lucentUrl + "/register", {
|
||||||
.post(channel.lucentUrl + "/register", {
|
name: name,
|
||||||
name: name,
|
email: email,
|
||||||
email: email,
|
})
|
||||||
})
|
|
||||||
.then(() => {
|
.then(() => {
|
||||||
window.location = channel.lucentUrl + "/login";
|
window.location = channel.lucentUrl + "/login";
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
errorMessage = error.response?.data.error;
|
errorMessage = error.response?.data.error;
|
||||||
console.log({errorMessage});
|
console.log({ errorMessage });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="wrapper-tiny">
|
<div class="wrapper-tiny">
|
||||||
<ErrorAlert message={errorMessage}/>
|
<ErrorAlert message={errorMessage} />
|
||||||
|
|
||||||
<form on:submit={register}>
|
<form on:submit={register}>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="name" class="form-label">Name</label>
|
<label for="name" class="form-label">Name</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
bind:value={name}
|
bind:value={name}
|
||||||
class="form-control"
|
class="form-control"
|
||||||
id="name"
|
id="name"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="email" class="form-label">Email address</label>
|
<label for="email" class="form-label">Email address</label>
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
bind:value={email}
|
bind:value={email}
|
||||||
class="form-control"
|
class="form-control"
|
||||||
id="email"
|
id="email"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="text-center mt-5 d-block">
|
<div class="text-center mt-5 d-block">
|
||||||
<SpinnerButton label="Register"/>
|
<SpinnerButton label="Register" />
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,139 @@
|
|||||||
|
/* eslint-disable no-undefined,no-param-reassign,no-shadow */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throttle execution of a function. Especially useful for rate limiting
|
||||||
|
* execution of handlers on events like resize and scroll.
|
||||||
|
*
|
||||||
|
* @param {number} delay - A zero-or-greater delay in milliseconds. For event callbacks, values around 100 or 250 (or even higher)
|
||||||
|
* are most useful.
|
||||||
|
* @param {Function} callback - A function to be executed after delay milliseconds. The `this` context and all arguments are passed through,
|
||||||
|
* as-is, to `callback` when the throttled-function is executed.
|
||||||
|
* @param {object} [options] - An object to configure options.
|
||||||
|
* @param {boolean} [options.noTrailing] - Optional, defaults to false. If noTrailing is true, callback will only execute every `delay` milliseconds
|
||||||
|
* while the throttled-function is being called. If noTrailing is false or unspecified, callback will be executed
|
||||||
|
* one final time after the last throttled-function call. (After the throttled-function has not been called for
|
||||||
|
* `delay` milliseconds, the internal counter is reset).
|
||||||
|
* @param {boolean} [options.noLeading] - Optional, defaults to false. If noLeading is false, the first throttled-function call will execute callback
|
||||||
|
* immediately. If noLeading is true, the first the callback execution will be skipped. It should be noted that
|
||||||
|
* callback will never executed if both noLeading = true and noTrailing = true.
|
||||||
|
* @param {boolean} [options.debounceMode] - If `debounceMode` is true (at begin), schedule `clear` to execute after `delay` ms. If `debounceMode` is
|
||||||
|
* false (at end), schedule `callback` to execute after `delay` ms.
|
||||||
|
*
|
||||||
|
* @returns {Function} A new, throttled, function.
|
||||||
|
*/
|
||||||
|
export function throttle(delay, callback, options) {
|
||||||
|
const {
|
||||||
|
noTrailing = false,
|
||||||
|
noLeading = false,
|
||||||
|
debounceMode = undefined,
|
||||||
|
} = options || {};
|
||||||
|
/*
|
||||||
|
* After wrapper has stopped being called, this timeout ensures that
|
||||||
|
* `callback` is executed at the proper times in `throttle` and `end`
|
||||||
|
* debounce modes.
|
||||||
|
*/
|
||||||
|
let timeoutID;
|
||||||
|
let cancelled = false;
|
||||||
|
|
||||||
|
// Keep track of the last time `callback` was executed.
|
||||||
|
let lastExec = 0;
|
||||||
|
|
||||||
|
// Function to clear existing timeout
|
||||||
|
function clearExistingTimeout() {
|
||||||
|
if (timeoutID) {
|
||||||
|
clearTimeout(timeoutID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to cancel next exec
|
||||||
|
function cancel(options) {
|
||||||
|
const { upcomingOnly = false } = options || {};
|
||||||
|
clearExistingTimeout();
|
||||||
|
cancelled = !upcomingOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The `wrapper` function encapsulates all of the throttling / debouncing
|
||||||
|
* functionality and when executed will limit the rate at which `callback`
|
||||||
|
* is executed.
|
||||||
|
*/
|
||||||
|
function wrapper(...arguments_) {
|
||||||
|
let self = this;
|
||||||
|
let elapsed = Date.now() - lastExec;
|
||||||
|
|
||||||
|
if (cancelled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute `callback` and update the `lastExec` timestamp.
|
||||||
|
function exec() {
|
||||||
|
lastExec = Date.now();
|
||||||
|
callback.apply(self, arguments_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If `debounceMode` is true (at begin) this is used to clear the flag
|
||||||
|
* to allow future `callback` executions.
|
||||||
|
*/
|
||||||
|
function clear() {
|
||||||
|
timeoutID = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!noLeading && debounceMode && !timeoutID) {
|
||||||
|
/*
|
||||||
|
* Since `wrapper` is being called for the first time and
|
||||||
|
* `debounceMode` is true (at begin), execute `callback`
|
||||||
|
* and noLeading != true.
|
||||||
|
*/
|
||||||
|
exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
clearExistingTimeout();
|
||||||
|
|
||||||
|
if (debounceMode === undefined && elapsed > delay) {
|
||||||
|
if (noLeading) {
|
||||||
|
/*
|
||||||
|
* In throttle mode with noLeading, if `delay` time has
|
||||||
|
* been exceeded, update `lastExec` and schedule `callback`
|
||||||
|
* to execute after `delay` ms.
|
||||||
|
*/
|
||||||
|
lastExec = Date.now();
|
||||||
|
if (!noTrailing) {
|
||||||
|
timeoutID = setTimeout(debounceMode ? clear : exec, delay);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* In throttle mode without noLeading, if `delay` time has been exceeded, execute
|
||||||
|
* `callback`.
|
||||||
|
*/
|
||||||
|
exec();
|
||||||
|
}
|
||||||
|
} else if (noTrailing !== true) {
|
||||||
|
/*
|
||||||
|
* In trailing throttle mode, since `delay` time has not been
|
||||||
|
* exceeded, schedule `callback` to execute `delay` ms after most
|
||||||
|
* recent execution.
|
||||||
|
*
|
||||||
|
* If `debounceMode` is true (at begin), schedule `clear` to execute
|
||||||
|
* after `delay` ms.
|
||||||
|
*
|
||||||
|
* If `debounceMode` is false (at end), schedule `callback` to
|
||||||
|
* execute after `delay` ms.
|
||||||
|
*/
|
||||||
|
timeoutID = setTimeout(
|
||||||
|
debounceMode ? clear : exec,
|
||||||
|
debounceMode === undefined ? delay - elapsed : delay,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapper.cancel = cancel;
|
||||||
|
|
||||||
|
// Return the wrapper function.
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function debounce(delay, callback, options) {
|
||||||
|
const { atBegin = false } = options || {};
|
||||||
|
return throttle(delay, callback, { debounceMode: atBegin !== false });
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import {getContext, onMount} from "svelte";
|
import { getContext, onMount } from "svelte";
|
||||||
import axios from "axios";
|
import { apiPost } from "../../helpers";
|
||||||
|
|
||||||
const channel = getContext("channel");
|
const channel = getContext("channel");
|
||||||
export let title;
|
export let title;
|
||||||
export let command;
|
export let command;
|
||||||
@@ -12,59 +11,57 @@
|
|||||||
let inProgress = false;
|
let inProgress = false;
|
||||||
|
|
||||||
function connect() {
|
function connect() {
|
||||||
const eventSource = new EventSource(channel.lucentUrl + "/command-report-source/" + command.signature );
|
const eventSource = new EventSource(
|
||||||
|
channel.lucentUrl + "/command-report-source/" + command.signature,
|
||||||
|
);
|
||||||
|
|
||||||
eventSource.onmessage = function (event) {
|
eventSource.onmessage = function (event) {
|
||||||
inProgress = true;
|
inProgress = true;
|
||||||
const data = JSON.parse(event.data);
|
const data = JSON.parse(event.data);
|
||||||
date = data.date;
|
date = data.date;
|
||||||
logs = data.logs;
|
logs = data.logs;
|
||||||
anchorEl.scrollIntoView()
|
anchorEl.scrollIntoView();
|
||||||
}
|
};
|
||||||
eventSource.onerror = (e) => {
|
eventSource.onerror = (e) => {
|
||||||
console.log(e)
|
console.log(e);
|
||||||
eventSource.close();
|
eventSource.close();
|
||||||
inProgress = false;
|
inProgress = false;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildWebsite(e) {
|
function buildWebsite(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
inProgress = true;
|
inProgress = true;
|
||||||
axios.post(channel.lucentUrl + "/command/" + command.signature).then(response => {
|
apiPost(channel.lucentUrl + "/command/" + command.signature).then(
|
||||||
connect()
|
(response) => {
|
||||||
})
|
connect();
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
connect()
|
connect();
|
||||||
})
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="common-wrapper">
|
<div class="common-wrapper">
|
||||||
<div class="lx-card mt-5">
|
<div class="lx-card mt-5">
|
||||||
|
|
||||||
<h3 class="header-small mb-5">{title}</h3>
|
<h3 class="header-small mb-5">{title}</h3>
|
||||||
|
|
||||||
<button on:click={buildWebsite} class="button primary mb-3" disabled={inProgress}>Start
|
<button
|
||||||
|
on:click={buildWebsite}
|
||||||
|
class="button primary mb-3"
|
||||||
|
disabled={inProgress}
|
||||||
|
>Start
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
{#if inProgress}
|
{#if inProgress}
|
||||||
<span class="badge text-bg-warning">
|
<span class="badge text-bg-warning"> Action in progress </span>
|
||||||
Action in progress
|
|
||||||
</span>
|
|
||||||
{/if}
|
{/if}
|
||||||
{#if !inProgress && logs}
|
{#if !inProgress && logs}
|
||||||
<span class="badge text-bg-info">
|
<span class="badge text-bg-info"> Action completed </span>
|
||||||
Action completed
|
|
||||||
</span>
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<pre class="logs">{logs}
|
<pre class="logs">{logs}
|
||||||
@@ -72,12 +69,13 @@
|
|||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.logs{
|
.logs {
|
||||||
max-height: 70vh;
|
max-height: 70vh;
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
background: var(--p90);
|
background: var(--p90);
|
||||||
color: var(--p10);
|
color: var(--p10);
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { createEventDispatcher } from "svelte";
|
import { createEventDispatcher } from "svelte";
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
import { range } from "lodash";
|
|
||||||
import NavItem from "./NavItem.svelte";
|
import NavItem from "./NavItem.svelte";
|
||||||
export let inModal;
|
export let inModal;
|
||||||
export let modalUrl;
|
export let modalUrl;
|
||||||
@@ -11,7 +11,11 @@
|
|||||||
|
|
||||||
$: totalPages = Math.ceil(total / limit);
|
$: totalPages = Math.ceil(total / limit);
|
||||||
$: currentPage = Math.ceil((skip - 1) / limit) + 1;
|
$: currentPage = Math.ceil((skip - 1) / limit) + 1;
|
||||||
|
const range = (start, end, step = 1) =>
|
||||||
|
Array.from(
|
||||||
|
{ length: Math.ceil((end - start) / step) },
|
||||||
|
(_, i) => start + i * step,
|
||||||
|
);
|
||||||
$: pageRange = range(currentPage - 3, currentPage + 4).filter((i) => {
|
$: pageRange = range(currentPage - 3, currentPage + 4).filter((i) => {
|
||||||
return i > 0 && i <= totalPages;
|
return i > 0 && i <= totalPages;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import { createEventDispatcher, getContext } from "svelte";
|
import { createEventDispatcher, getContext } from "svelte";
|
||||||
import { debounce } from "lodash";
|
import { apiGet, debounce } from "../../../helpers";
|
||||||
|
|
||||||
const channel = getContext("channel");
|
const channel = getContext("channel");
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
@@ -12,17 +12,16 @@
|
|||||||
$: searchOptions = [];
|
$: searchOptions = [];
|
||||||
|
|
||||||
const updateResults = debounce((e) => {
|
const updateResults = debounce((e) => {
|
||||||
axios
|
apiGet(channel.lucentUrl + "/records/suggestions", {
|
||||||
.get(channel.lucentUrl + "/records/suggestions", {
|
params: {
|
||||||
params: {
|
schema: field.collections[0],
|
||||||
schema: field.collections[0],
|
field: "search",
|
||||||
field: "search",
|
value: search,
|
||||||
value: search,
|
ui: "search",
|
||||||
ui: "search",
|
},
|
||||||
},
|
})
|
||||||
})
|
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
searchOptions = response.data;
|
searchOptions = response;
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
searchOptions = [];
|
searchOptions = [];
|
||||||
|
|||||||
@@ -1,129 +0,0 @@
|
|||||||
<script>
|
|
||||||
import {onDestroy, onMount} from "svelte";
|
|
||||||
|
|
||||||
import tinymce from "tinymce/tinymce";
|
|
||||||
import "tinymce/models/dom";
|
|
||||||
import "tinymce/icons/default";
|
|
||||||
import "tinymce/themes/silver";
|
|
||||||
import "tinymce/skins/ui/oxide/skin.css";
|
|
||||||
import contentUiSkinCss from "tinymce/skins/ui/oxide/content.css?inline";
|
|
||||||
import customcss from "./tinymce.css?inline";
|
|
||||||
|
|
||||||
import "tinymce/plugins/link";
|
|
||||||
import "tinymce/plugins/code";
|
|
||||||
import "tinymce/plugins/image";
|
|
||||||
import "tinymce/plugins/table";
|
|
||||||
import "tinymce/plugins/codesample";
|
|
||||||
import "tinymce/plugins/media";
|
|
||||||
|
|
||||||
import "tinymce/plugins/lists";
|
|
||||||
import "tinymce/plugins/autoresize";
|
|
||||||
import "tinymce/plugins/wordcount";
|
|
||||||
|
|
||||||
export let value = "";
|
|
||||||
export let additionalConfig = {};
|
|
||||||
let lastVal = "";
|
|
||||||
let textareaEl;
|
|
||||||
let activeEditor;
|
|
||||||
let editorWrapper;
|
|
||||||
const plugins = [
|
|
||||||
"autoresize",
|
|
||||||
"code",
|
|
||||||
"image",
|
|
||||||
"table",
|
|
||||||
"codesample",
|
|
||||||
"link",
|
|
||||||
"lists",
|
|
||||||
"media",
|
|
||||||
"wordcount",
|
|
||||||
];
|
|
||||||
const toolbar =
|
|
||||||
"bold italic underline strikethrough removeformat | link | subscript superscript bullist numlist media image codesample table code wordcount blockquote indent outdent blocks";
|
|
||||||
|
|
||||||
onDestroy(() => {
|
|
||||||
if (activeEditor) {
|
|
||||||
activeEditor.destroy();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
const config = {
|
|
||||||
target: textareaEl,
|
|
||||||
toolbar_mode: "sliding",
|
|
||||||
toolbar_sticky: true,
|
|
||||||
skin: false,
|
|
||||||
content_css: false,
|
|
||||||
content_style: contentUiSkinCss.toString() + customcss.toString(),
|
|
||||||
branding: false,
|
|
||||||
inline: false,
|
|
||||||
plugins: plugins,
|
|
||||||
contextmenu: false,
|
|
||||||
menubar: false,
|
|
||||||
statusbar: false,
|
|
||||||
entity_encoding: "raw",
|
|
||||||
convert_urls: false,
|
|
||||||
toolbar: toolbar,
|
|
||||||
image_caption: true,
|
|
||||||
relative_urls: false,
|
|
||||||
browser_spellcheck: true,
|
|
||||||
max_height: 600,
|
|
||||||
// media_poster: false,
|
|
||||||
|
|
||||||
setup: function (editor) {
|
|
||||||
activeEditor = editor;
|
|
||||||
|
|
||||||
editor.on("init", function (e) {
|
|
||||||
editor.setContent(value ?? "");
|
|
||||||
});
|
|
||||||
|
|
||||||
// editor.on("blur", function (e) {
|
|
||||||
// let content = setImageDimensions(editor.getContent());
|
|
||||||
// dispatch("editorBlur", content);
|
|
||||||
// editorWrapper.classList.remove("editorFocus");
|
|
||||||
// // return false;
|
|
||||||
// });
|
|
||||||
|
|
||||||
// editor.on("focus", function (e) {
|
|
||||||
// editorWrapper.classList.add("editorFocus");
|
|
||||||
// // return false;
|
|
||||||
// });
|
|
||||||
|
|
||||||
editor.on("change input undo redo", function (e) {
|
|
||||||
lastVal = editor.getContent();
|
|
||||||
if (lastVal !== value) {
|
|
||||||
value = lastVal;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
tinymce.init({...config, ...additionalConfig});
|
|
||||||
});
|
|
||||||
|
|
||||||
export function insertMedia(info){
|
|
||||||
activeEditor.execCommand('InsertHTML', false, info.html);
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div bind:this={editorWrapper} class="tox-wrapper">
|
|
||||||
<div class="form-control" bind:this={textareaEl}>
|
|
||||||
{@html value}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
:global(.tox:not(.tox-tinymce-inline) .tox-editor-header) {
|
|
||||||
background-color: #fff;
|
|
||||||
border-bottom: 1px solid #ced4da;
|
|
||||||
box-shadow: none;
|
|
||||||
padding: 4px 0;
|
|
||||||
transition: box-shadow 0.5s;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.tox-tinymce) {
|
|
||||||
border: 1px solid #ced4da;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
</style>
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
.mce-content-body .img {
|
|
||||||
max-width: 100%;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mce-content-body{
|
|
||||||
font-size: 16px;
|
|
||||||
line-height: 20px;
|
|
||||||
}
|
|
||||||
.mce-content-body p{
|
|
||||||
|
|
||||||
margin-bottom: 14px;
|
|
||||||
&:last-child{
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mce-content-body ul {
|
|
||||||
padding: 0 0 0 16px;
|
|
||||||
list-style: none outside none;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.mce-content-body li::before {
|
|
||||||
content: "—";
|
|
||||||
opacity: .5;
|
|
||||||
font-size: 12px;
|
|
||||||
padding-right: 6px;
|
|
||||||
vertical-align: 10%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mce-content-body li {
|
|
||||||
list-style: none;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
@@ -3,9 +3,8 @@
|
|||||||
import SuccessAlert from "../common/SuccessAlert.svelte";
|
import SuccessAlert from "../common/SuccessAlert.svelte";
|
||||||
import SpinnerButton from "../common/SpinnerButton.svelte";
|
import SpinnerButton from "../common/SpinnerButton.svelte";
|
||||||
import MemberSettingsCard from "./MemberSettingsCard.svelte";
|
import MemberSettingsCard from "./MemberSettingsCard.svelte";
|
||||||
import {getContext} from "svelte";
|
import { getContext } from "svelte";
|
||||||
import axios from "axios";
|
import { apiPost } from "../../helpers";
|
||||||
|
|
||||||
|
|
||||||
const channel = getContext("channel");
|
const channel = getContext("channel");
|
||||||
export let users;
|
export let users;
|
||||||
@@ -23,15 +22,14 @@
|
|||||||
function invite(newName, newEmail, newRole) {
|
function invite(newName, newEmail, newRole) {
|
||||||
errorMessage = "";
|
errorMessage = "";
|
||||||
|
|
||||||
axios
|
apiPost(channel.lucentUrl + "/members/invite", {
|
||||||
.post(channel.lucentUrl + "/members/invite", {
|
name: newName,
|
||||||
name: newName,
|
email: newEmail,
|
||||||
email: newEmail,
|
roles: [newRole],
|
||||||
roles: [newRole],
|
})
|
||||||
})
|
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
successAlert.show("User was invited");
|
successAlert.show("User was invited");
|
||||||
users = [...users, response.data.user];
|
users = [...users, response.user];
|
||||||
name = null;
|
name = null;
|
||||||
email = null;
|
email = null;
|
||||||
role = null;
|
role = null;
|
||||||
@@ -45,14 +43,13 @@
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
errorMessage = "";
|
errorMessage = "";
|
||||||
|
|
||||||
axios
|
apiPost(channel.lucentUrl + "/members/update", {
|
||||||
.post(channel.lucentUrl + "/members/update", {
|
id: e.detail.user,
|
||||||
id: e.detail.user,
|
roles: e.detail.roles,
|
||||||
roles: e.detail.roles,
|
})
|
||||||
})
|
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
successAlert.show("Users updated");
|
successAlert.show("Users updated");
|
||||||
users = response.data.users;
|
users = response.users;
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
errorMessage = error.response?.data?.error ?? "";
|
errorMessage = error.response?.data?.error ?? "";
|
||||||
@@ -63,50 +60,45 @@
|
|||||||
<div class="common-wrapper">
|
<div class="common-wrapper">
|
||||||
<div class="lx-card mt-5">
|
<div class="lx-card mt-5">
|
||||||
<h3 class="header-small mb-5">Invite people</h3>
|
<h3 class="header-small mb-5">Invite people</h3>
|
||||||
<ErrorAlert message={errorMessage}/>
|
<ErrorAlert message={errorMessage} />
|
||||||
<SuccessAlert bind:this={successAlert}/>
|
<SuccessAlert bind:this={successAlert} />
|
||||||
|
|
||||||
<form on:submit={submitInvite}>
|
<form on:submit={submitInvite}>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="inviteeName" class="form-label"
|
<label for="inviteeName" class="form-label">Invitee Name</label>
|
||||||
>Invitee Name</label
|
|
||||||
>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
bind:value={name}
|
bind:value={name}
|
||||||
class="form-control"
|
class="form-control"
|
||||||
id="inviteeName"
|
id="inviteeName"
|
||||||
placeholder="Member name"
|
placeholder="Member name"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="inviteeEmail" class="form-label"
|
<label for="inviteeEmail" class="form-label"
|
||||||
>Invitee Email Address</label
|
>Invitee Email Address</label
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
bind:value={email}
|
bind:value={email}
|
||||||
class="form-control"
|
class="form-control"
|
||||||
id="inviteeEmail"
|
id="inviteeEmail"
|
||||||
placeholder="Member email"
|
placeholder="Member email"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="me-3">
|
<div class="me-3">
|
||||||
<select bind:value={role}>
|
<select bind:value={role}>
|
||||||
{#each channel.roles.filter((r) => r !== "removed") as arole}
|
{#each channel.roles.filter((r) => r !== "removed") as arole}
|
||||||
<option
|
<option value={arole}>{arole}</option>
|
||||||
value={arole}
|
|
||||||
|
|
||||||
>{arole}</option>
|
|
||||||
{/each}
|
{/each}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-5 d-block text-center">
|
<div class="mt-5 d-block text-center">
|
||||||
<SpinnerButton label="Invite"/>
|
<SpinnerButton label="Invite" />
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -115,10 +107,10 @@
|
|||||||
<h3 class="header-small mb-5 mt-5">Members</h3>
|
<h3 class="header-small mb-5 mt-5">Members</h3>
|
||||||
{#each users as user}
|
{#each users as user}
|
||||||
<MemberSettingsCard
|
<MemberSettingsCard
|
||||||
member={user}
|
member={user}
|
||||||
roles={channel.roles}
|
roles={channel.roles}
|
||||||
on:update={update}
|
on:update={update}
|
||||||
on:reinvite={(e) => invite(e.detail.email, e.detail.role)}
|
on:reinvite={(e) => invite(e.detail.email, e.detail.role)}
|
||||||
/>
|
/>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
<script>
|
<script>
|
||||||
import { afterUpdate, getContext, onMount } from "svelte";
|
import { afterUpdate, getContext, onMount } from "svelte";
|
||||||
import { isEqual } from "lodash";
|
|
||||||
import axios from "axios";
|
|
||||||
import EditHeader from "./header/EditHeader.svelte";
|
import EditHeader from "./header/EditHeader.svelte";
|
||||||
import ContentTabs from "./header/ContentTabs.svelte";
|
import ContentTabs from "./header/ContentTabs.svelte";
|
||||||
import FormField from "./FormField.svelte";
|
import FormField from "./FormField.svelte";
|
||||||
@@ -9,6 +7,7 @@
|
|||||||
import Info from "./Info.svelte";
|
import Info from "./Info.svelte";
|
||||||
import ErrorAlert from "../common/ErrorAlert.svelte";
|
import ErrorAlert from "../common/ErrorAlert.svelte";
|
||||||
import Title from "./header/Title.svelte";
|
import Title from "./header/Title.svelte";
|
||||||
|
import { apiPost, isEqual } from "../../helpers";
|
||||||
|
|
||||||
const channel = getContext("channel");
|
const channel = getContext("channel");
|
||||||
|
|
||||||
@@ -97,12 +96,11 @@
|
|||||||
graph.edges = graph.edges?.filter(
|
graph.edges = graph.edges?.filter(
|
||||||
(edge) => !edge._isTrashed && edge.source === record.id,
|
(edge) => !edge._isTrashed && edge.source === record.id,
|
||||||
);
|
);
|
||||||
axios
|
apiPost(channel.lucentUrl + "/records", {
|
||||||
.post(channel.lucentUrl + "/records", {
|
record: record,
|
||||||
record: record,
|
edges: graph.edges,
|
||||||
edges: graph.edges,
|
isCreateMode: isCreateMode,
|
||||||
isCreateMode: isCreateMode,
|
})
|
||||||
})
|
|
||||||
.then(function (response) {
|
.then(function (response) {
|
||||||
console.log("SAVE: SAVED");
|
console.log("SAVE: SAVED");
|
||||||
|
|
||||||
@@ -110,14 +108,14 @@
|
|||||||
window.location =
|
window.location =
|
||||||
channel.lucentUrl + "/records/" + record.id;
|
channel.lucentUrl + "/records/" + record.id;
|
||||||
} else {
|
} else {
|
||||||
record = response.data.records[0] ?? null;
|
record = response.records[0] ?? null;
|
||||||
if (!record) {
|
if (!record) {
|
||||||
// means trashed
|
// means trashed
|
||||||
hasUnsavedData = false;
|
hasUnsavedData = false;
|
||||||
window.location = channel.lucentUrl;
|
window.location = channel.lucentUrl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
graph = response.data;
|
graph = response;
|
||||||
setOriginalContent();
|
setOriginalContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<script>
|
<script>
|
||||||
import { friendlyDate } from "../../helpers";
|
import { friendlyDate, isEqual } from "../../helpers";
|
||||||
import Avatar from "../account/Avatar.svelte";
|
import Avatar from "../account/Avatar.svelte";
|
||||||
import { usernameById } from "../account/users";
|
import { usernameById } from "../account/users";
|
||||||
import { isEqual } from "lodash";
|
|
||||||
import Icon from "../common/Icon.svelte";
|
import Icon from "../common/Icon.svelte";
|
||||||
import RevisionCell from "./revisions/RevisionCell.svelte";
|
import RevisionCell from "./revisions/RevisionCell.svelte";
|
||||||
import { getContext } from "svelte";
|
import { getContext } from "svelte";
|
||||||
|
|||||||
@@ -6,14 +6,13 @@
|
|||||||
onMount,
|
onMount,
|
||||||
} from "svelte";
|
} from "svelte";
|
||||||
|
|
||||||
import { isEqual } from "lodash";
|
|
||||||
import FormField from "./FormField.svelte";
|
import FormField from "./FormField.svelte";
|
||||||
import FilePreview from "./FilePreview.svelte";
|
import FilePreview from "./FilePreview.svelte";
|
||||||
import ContentTabs from "./header/ContentTabs.svelte";
|
import ContentTabs from "./header/ContentTabs.svelte";
|
||||||
import ErrorAlert from "../common/ErrorAlert.svelte";
|
import ErrorAlert from "../common/ErrorAlert.svelte";
|
||||||
import EditHeader from "./header/EditHeader.svelte";
|
import EditHeader from "./header/EditHeader.svelte";
|
||||||
import axios from "axios";
|
|
||||||
import Title from "./header/Title.svelte";
|
import Title from "./header/Title.svelte";
|
||||||
|
import { apiPost, isEqual } from "../../helpers";
|
||||||
|
|
||||||
const channel = getContext("channel");
|
const channel = getContext("channel");
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
@@ -118,17 +117,16 @@
|
|||||||
(edge) => !edge._isTrashed && edge.source === record.id,
|
(edge) => !edge._isTrashed && edge.source === record.id,
|
||||||
) ?? [];
|
) ?? [];
|
||||||
|
|
||||||
axios
|
apiPost(channel.lucentUrl + "/records", {
|
||||||
.post(channel.lucentUrl + "/records", {
|
record: record,
|
||||||
record: record,
|
edges: graph.edges,
|
||||||
edges: graph.edges,
|
isCreateMode: isCreateMode,
|
||||||
isCreateMode: isCreateMode,
|
})
|
||||||
})
|
|
||||||
.then(function (response) {
|
.then(function (response) {
|
||||||
console.log("SAVE: SAVED INLINE");
|
console.log("SAVE: SAVED INLINE");
|
||||||
|
|
||||||
record = response.data.records[0];
|
record = response.records[0];
|
||||||
graph = response.data;
|
graph = response;
|
||||||
if (!isCreateMode) {
|
if (!isCreateMode) {
|
||||||
setOriginalContent();
|
setOriginalContent();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
<script>
|
<script>
|
||||||
import {getContext} from "svelte";
|
import { getContext } from "svelte";
|
||||||
import {insertEdges} from "./reference";
|
import { insertEdges } from "./reference";
|
||||||
import {getErrorMessage} from "./errorMessage";
|
import { getErrorMessage } from "./errorMessage";
|
||||||
import {sortByField} from "../../edges/sortEdges";
|
import { sortByField } from "../../edges/sortEdges";
|
||||||
import ReferenceInlineButtons from "./ReferenceInlineButtons.svelte";
|
import ReferenceInlineButtons from "./ReferenceInlineButtons.svelte";
|
||||||
import Sortable from "../../libs/Sortable.svelte";
|
import Sortable from "../../libs/Sortable.svelte";
|
||||||
import PreviewReference from "../previews/PreviewReference.svelte";
|
import PreviewReference from "../previews/PreviewReference.svelte";
|
||||||
import axios from "axios";
|
|
||||||
|
|
||||||
const channel = getContext("channel");
|
const channel = getContext("channel");
|
||||||
export let record;
|
export let record;
|
||||||
@@ -15,27 +14,36 @@
|
|||||||
export let validationErrors;
|
export let validationErrors;
|
||||||
$: errorMessage = getErrorMessage(validationErrors, field.name);
|
$: errorMessage = getErrorMessage(validationErrors, field.name);
|
||||||
|
|
||||||
|
$: references =
|
||||||
$: references = graph.edges
|
graph.edges
|
||||||
.filter((edge) => edge.field === field.name)
|
.filter((edge) => edge.field === field.name)
|
||||||
.map((edge) => {
|
.map((edge) => {
|
||||||
return graph.records.find((increc) => increc.id === edge.target && record.id === edge.source);
|
return graph.records.find(
|
||||||
}).filter((rec) => (rec?.id ? true : false)) ?? [];
|
(increc) =>
|
||||||
|
increc.id === edge.target && record.id === edge.source,
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.filter((rec) => (rec?.id ? true : false)) ?? [];
|
||||||
|
|
||||||
let collections = channel.schemas.filter((aschema) =>
|
let collections = channel.schemas.filter((aschema) =>
|
||||||
field.collections.includes(aschema.name)
|
field.collections.includes(aschema.name),
|
||||||
);
|
);
|
||||||
|
|
||||||
function removeReference(e) {
|
function removeReference(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
graph.edges = graph.edges.filter(
|
graph.edges = graph.edges.filter(
|
||||||
(edge) => !(edge.target === e.detail && edge.field === field.name)
|
(edge) => !(edge.target === e.detail && edge.field === field.name),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function reorder(e) {
|
function reorder(e) {
|
||||||
|
graph.edges = sortByField(
|
||||||
graph.edges = sortByField(e.detail.source, e.detail.target, graph.edges, field.name, references);
|
e.detail.source,
|
||||||
|
e.detail.target,
|
||||||
|
graph.edges,
|
||||||
|
field.name,
|
||||||
|
references,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function insert(e) {
|
function insert(e) {
|
||||||
@@ -49,9 +57,14 @@
|
|||||||
// }).then(function (response) {
|
// }).then(function (response) {
|
||||||
// graph = response.data.graph;
|
// graph = response.data.graph;
|
||||||
// })
|
// })
|
||||||
graph = insertEdges(graph, record, e.detail.records, field.name, e.detail.action);
|
graph = insertEdges(
|
||||||
|
graph,
|
||||||
|
record,
|
||||||
|
e.detail.records,
|
||||||
|
field.name,
|
||||||
|
e.detail.action,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if errorMessage}
|
{#if errorMessage}
|
||||||
@@ -61,10 +74,10 @@
|
|||||||
{/if}
|
{/if}
|
||||||
<div class="inline-card-wrapper">
|
<div class="inline-card-wrapper">
|
||||||
<ReferenceInlineButtons
|
<ReferenceInlineButtons
|
||||||
recordId={null}
|
recordId={null}
|
||||||
schemas={collections}
|
schemas={collections}
|
||||||
on:insert={insert}
|
on:insert={insert}
|
||||||
on:save={insert}
|
on:save={insert}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{#if references.length > 0}
|
{#if references.length > 0}
|
||||||
@@ -72,10 +85,10 @@
|
|||||||
{#each references as reference (reference.id)}
|
{#each references as reference (reference.id)}
|
||||||
<div>
|
<div>
|
||||||
<PreviewReference
|
<PreviewReference
|
||||||
{graph}
|
{graph}
|
||||||
record={reference}
|
record={reference}
|
||||||
hasDelete={true}
|
hasDelete={true}
|
||||||
on:remove={removeReference}
|
on:remove={removeReference}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<script>
|
<script>
|
||||||
import {createEventDispatcher, getContext} from "svelte";
|
import { createEventDispatcher, getContext } from "svelte";
|
||||||
import Icon from "../../common/Icon.svelte";
|
import Icon from "../../common/Icon.svelte";
|
||||||
import InlineEdit from "../InlineEdit.svelte";
|
import InlineEdit from "../InlineEdit.svelte";
|
||||||
import Dialog from "../../dialog/Dialog.svelte";
|
import Dialog from "../../dialog/Dialog.svelte";
|
||||||
import DialogRecord from "../../dialog/DialogRecord.svelte";
|
import DialogRecord from "../../dialog/DialogRecord.svelte";
|
||||||
import axios from "axios";
|
|
||||||
import Dropdown from "../../common/Dropdown.svelte";
|
import Dropdown from "../../common/Dropdown.svelte";
|
||||||
|
import { apiGet } from "../../../helpers";
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
const channel = getContext("channel");
|
const channel = getContext("channel");
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
console.log("Save inline");
|
console.log("Save inline");
|
||||||
inLineCreateRecord = null;
|
inLineCreateRecord = null;
|
||||||
dialogRecord.close()
|
dialogRecord.close();
|
||||||
dispatch("save", {
|
dispatch("save", {
|
||||||
records: e.detail.records,
|
records: e.detail.records,
|
||||||
after: recordId,
|
after: recordId,
|
||||||
@@ -44,11 +44,10 @@
|
|||||||
function createInlineReference(e, schemaUId) {
|
function createInlineReference(e, schemaUId) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
inLineCreateRecord = null;
|
inLineCreateRecord = null;
|
||||||
axios
|
apiGet(channel.lucentUrl + "/records/newInline?schema=" + schemaUId)
|
||||||
.get(channel.lucentUrl + "/records/newInline?schema=" + schemaUId)
|
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
inLineCreateRecord = response.data;
|
inLineCreateRecord = response;
|
||||||
dialogRecord.open()
|
dialogRecord.open();
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
@@ -57,60 +56,53 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if schemas.length > 1}
|
{#if schemas.length > 1}
|
||||||
<div
|
<div style="display: flex;align-items: center;gap:4px">
|
||||||
style="display: flex;align-items: center;gap:4px"
|
|
||||||
>
|
|
||||||
<Dropdown>
|
<Dropdown>
|
||||||
<div slot="button">New</div>
|
<div slot="button">New</div>
|
||||||
{#each schemas as schema}
|
{#each schemas as schema}
|
||||||
<button
|
<button
|
||||||
class=" button"
|
class=" button"
|
||||||
on:click={(e) =>
|
on:click={(e) => createInlineReference(e, schema.name)}
|
||||||
createInlineReference(e, schema.name)}
|
>{schema.label}
|
||||||
>{schema.label}
|
|
||||||
</button>
|
</button>
|
||||||
{/each}
|
{/each}
|
||||||
|
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
<Dropdown>
|
<Dropdown>
|
||||||
<div slot="button"> <Icon icon="magnifying-glass"/></div>
|
<div slot="button"><Icon icon="magnifying-glass" /></div>
|
||||||
{#each schemas as schema}
|
{#each schemas as schema}
|
||||||
<button
|
<button
|
||||||
class="button"
|
class="button"
|
||||||
on:click={(e) => openBrowseModal(e, schema.name)}
|
on:click={(e) => openBrowseModal(e, schema.name)}
|
||||||
>{schema.label}
|
>{schema.label}
|
||||||
</button>
|
</button>
|
||||||
{/each}
|
{/each}
|
||||||
|
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div style="display:flex;align-items: center;gap: 4px">
|
<div style="display:flex;align-items: center;gap: 4px">
|
||||||
<button
|
<button
|
||||||
class="button"
|
class="button"
|
||||||
on:click={(e) => createInlineReference(e, schemas[0].name)}
|
on:click={(e) => createInlineReference(e, schemas[0].name)}
|
||||||
>New
|
>New
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="button"
|
class="button"
|
||||||
on:click={(e) => openBrowseModal(e, schemas[0].name)}
|
on:click={(e) => openBrowseModal(e, schemas[0].name)}
|
||||||
>
|
|
||||||
<Icon icon="magnifying-glass"/>
|
|
||||||
</button
|
|
||||||
>
|
>
|
||||||
|
<Icon icon="magnifying-glass" />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<DialogRecord bind:this={dialogRecord}>
|
<DialogRecord bind:this={dialogRecord}>
|
||||||
{#if inLineCreateRecord}
|
{#if inLineCreateRecord}
|
||||||
<InlineEdit
|
<InlineEdit
|
||||||
{...inLineCreateRecord}
|
{...inLineCreateRecord}
|
||||||
isCreateMode={true}
|
isCreateMode={true}
|
||||||
on:cancel={(e) => (inLineCreateRecord = null)}
|
on:cancel={(e) => (inLineCreateRecord = null)}
|
||||||
on:inlinesaved={save}
|
on:inlinesaved={save}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/if}
|
{/if}
|
||||||
</DialogRecord>
|
</DialogRecord>
|
||||||
|
|
||||||
<Dialog bind:this={browseModal} on:insert={insert}/>
|
<Dialog bind:this={browseModal} on:insert={insert} />
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<script>
|
<script>
|
||||||
import { getContext } from "svelte";
|
import { getContext } from "svelte";
|
||||||
import { debounce } from "lodash";
|
|
||||||
import { getErrorMessage } from "./errorMessage";
|
import { getErrorMessage } from "./errorMessage";
|
||||||
import { insertEdges } from "./reference.js";
|
import { insertEdges } from "./reference.js";
|
||||||
import Icon from "../../common/Icon.svelte";
|
import Icon from "../../common/Icon.svelte";
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import Tinymce from "../../libs/Tinymce.svelte";
|
|
||||||
import RichEditorFiles from "./RichEditorFiles.svelte";
|
import RichEditorFiles from "./RichEditorFiles.svelte";
|
||||||
import {getErrorMessage} from "./errorMessage";
|
import { getErrorMessage } from "./errorMessage";
|
||||||
import Trix from "../../libs/Trix.svelte";
|
import Trix from "../../libs/Trix.svelte";
|
||||||
|
|
||||||
export let value;
|
export let value;
|
||||||
@@ -18,29 +17,24 @@
|
|||||||
readonly: field.readonly && !isCreateMode,
|
readonly: field.readonly && !isCreateMode,
|
||||||
};
|
};
|
||||||
|
|
||||||
function insertMedia(e){
|
function insertMedia(e) {
|
||||||
editor.insertMedia(e.detail)
|
editor.insertMedia(e.detail);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="mb-0">
|
<div class="mb-0">
|
||||||
|
|
||||||
<Trix {field} bind:this={editor} bind:value></Trix>
|
<Trix {field} bind:this={editor} bind:value></Trix>
|
||||||
<!-- <Tinymce bind:this={editor} bind:value {additionalConfig}/>-->
|
<!-- <Tinymce bind:this={editor} bind:value {additionalConfig}/>-->
|
||||||
{#if field.collections.length > 0}
|
{#if field.collections.length > 0}
|
||||||
<RichEditorFiles
|
<RichEditorFiles
|
||||||
bind:graph
|
bind:graph
|
||||||
{record}
|
{record}
|
||||||
{field}
|
{field}
|
||||||
{validationErrors}
|
{validationErrors}
|
||||||
on:editor-insert={insertMedia}
|
on:editor-insert={insertMedia}
|
||||||
>
|
></RichEditorFiles>
|
||||||
|
|
||||||
|
|
||||||
</RichEditorFiles>
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|
||||||
{#if errorMessage}
|
{#if errorMessage}
|
||||||
<div class="invalid-feedback d-block">
|
<div class="invalid-feedback d-block">
|
||||||
{errorMessage}
|
{errorMessage}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<script>
|
<script>
|
||||||
import { v4 as uuidv4 } from "uuid";
|
|
||||||
import { getContext } from "svelte";
|
import { getContext } from "svelte";
|
||||||
import Icon from "../../common/Icon.svelte";
|
import Icon from "../../common/Icon.svelte";
|
||||||
import { getErrorMessage } from "./errorMessage";
|
import { getErrorMessage } from "./errorMessage";
|
||||||
@@ -14,12 +13,11 @@
|
|||||||
|
|
||||||
function generateId(e) {
|
function generateId(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
value = uuidv4();
|
value = self.crypto.randomUUID();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="mb-0">
|
<div class="mb-0">
|
||||||
|
|
||||||
<div class="d-flex justify-content-between">
|
<div class="d-flex justify-content-between">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
@@ -31,13 +29,13 @@
|
|||||||
{readonly}
|
{readonly}
|
||||||
/>
|
/>
|
||||||
{#if !readonly}
|
{#if !readonly}
|
||||||
<button
|
<button
|
||||||
class="btn btn-primary ms-2"
|
class="btn btn-primary ms-2"
|
||||||
title="Generate a new UUIDv4"
|
title="Generate a new UUIDv4"
|
||||||
on:click={generateId}
|
on:click={generateId}
|
||||||
>
|
>
|
||||||
<Icon icon="dice" />
|
<Icon icon="dice" />
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
<script>
|
<script>
|
||||||
import { uniqueId } from "lodash";
|
|
||||||
import { getContext } from "svelte";
|
import { getContext } from "svelte";
|
||||||
const channelurl = getContext("channelurl");
|
const channelurl = getContext("channelurl");
|
||||||
export let field;
|
export let field;
|
||||||
export let value;
|
export let value;
|
||||||
export let schema;
|
export let schema;
|
||||||
let id = uniqueId();
|
let id = self.crypto.randomUUID();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="mb-0">
|
<div class="mb-0">
|
||||||
@@ -25,6 +24,6 @@
|
|||||||
placeholder="https://www.example.com"
|
placeholder="https://www.example.com"
|
||||||
/>
|
/>
|
||||||
{#if field.help}
|
{#if field.help}
|
||||||
<small class=" text-primary opacity-50">{field.help}</small>
|
<small class=" text-primary opacity-50">{field.help}</small>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,24 +1,36 @@
|
|||||||
import {uniqBy} from "lodash";
|
import { arrayUniqueCb } from "../../../helpers";
|
||||||
|
|
||||||
export function insertEdges(graph, sourceRecord, targetRecords, fieldName, action = "") {
|
export function insertEdges(
|
||||||
let newEdges = targetRecords.map((r) => {
|
graph,
|
||||||
return {
|
sourceRecord,
|
||||||
target: r.id,
|
targetRecords,
|
||||||
source: sourceRecord.id,
|
fieldName,
|
||||||
sourceSchema: sourceRecord.schema,
|
action = "",
|
||||||
targetSchema: r.schema,
|
) {
|
||||||
field: fieldName,
|
let newEdges = targetRecords.map((r) => {
|
||||||
depth: 1,
|
return {
|
||||||
rank: ""
|
target: r.id,
|
||||||
};
|
source: sourceRecord.id,
|
||||||
});
|
sourceSchema: sourceRecord.schema,
|
||||||
|
targetSchema: r.schema,
|
||||||
|
field: fieldName,
|
||||||
|
depth: 1,
|
||||||
|
rank: "",
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
let replacedEdges = graph.edges;
|
let replacedEdges = graph.edges;
|
||||||
if (action === "replace") {
|
if (action === "replace") {
|
||||||
replacedEdges = replacedEdges.filter((edge) => edge.field !== field.name);
|
replacedEdges = replacedEdges.filter((edge) => edge.field !== field.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
graph.records = uniqBy([...graph.records, ...targetRecords], (r) => r.id);
|
graph.records = arrayUniqueCb(
|
||||||
graph.edges = uniqBy([...replacedEdges, ...newEdges], (edge) => edge.source + edge.target + edge.field + edge.depth);
|
[...graph.records, ...targetRecords],
|
||||||
return graph;
|
(r) => r.id,
|
||||||
|
);
|
||||||
|
graph.edges = arrayUniqueCb(
|
||||||
|
[...replacedEdges, ...newEdges],
|
||||||
|
(edge) => edge.source + edge.target + edge.field + edge.depth,
|
||||||
|
);
|
||||||
|
return graph;
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+1695
-4376
File diff suppressed because it is too large
Load Diff
+27
-32
@@ -1,34 +1,29 @@
|
|||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vite build"
|
"build": "vite build"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@codemirror/commands": "^6.6.0",
|
"@codemirror/commands": "^6.6.0",
|
||||||
"@codemirror/lang-json": "^6.0.1",
|
"@codemirror/lang-json": "^6.0.1",
|
||||||
"@codemirror/lang-markdown": "^6.2.5",
|
"@codemirror/lang-markdown": "^6.2.5",
|
||||||
"@codemirror/state": "^6.4.1",
|
"@codemirror/state": "^6.4.1",
|
||||||
"@sveltejs/vite-plugin-svelte": "^3.1.1",
|
"@sveltejs/vite-plugin-svelte": "^3.1.1",
|
||||||
"axios": "^1.7.4",
|
"codemirror": "^6.0.1",
|
||||||
"codemirror": "^6.0.1",
|
"date-fns": "^3.6.0",
|
||||||
"date-fns": "^3.6.0",
|
"flatpickr": "^4.6.13",
|
||||||
"flatpickr": "^4.6.13",
|
"fuse.js": "^7.0.0",
|
||||||
"fuse.js": "^7.0.0",
|
"htmx.org": "^2.0.1",
|
||||||
"htmx.org": "^2.0.1",
|
"install": "^0.13.0",
|
||||||
"install": "^0.13.0",
|
"laravel-vite-plugin": "^1.0.5",
|
||||||
"laravel-vite-plugin": "^1.0.5",
|
"mustache": "^4.2.0",
|
||||||
"lodash": "^4.17.21",
|
"postcss": "8.4.31",
|
||||||
"mustache": "^4.2.0",
|
"sass": "^1.77.8",
|
||||||
"npm": "^10.8.2",
|
"sortablejs": "^1.15.2",
|
||||||
"postcss": "8.4.31",
|
"svelte": "^4.2.18",
|
||||||
"sass": "^1.77.8",
|
"trix": "^2.1.5",
|
||||||
"sortablejs": "^1.15.2",
|
"vite": "5.2.6"
|
||||||
"svelte": "^4.2.18",
|
}
|
||||||
"tinymce": "^6.8.4",
|
|
||||||
"trix": "^2.1.5",
|
|
||||||
"uuid": "^10.0.0",
|
|
||||||
"vite": "5.2.6"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,11 +7,8 @@ use Illuminate\Http\Request;
|
|||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
use Lucent\Channel\ChannelService;
|
use Lucent\Channel\ChannelService;
|
||||||
use Lucent\File\FileService;
|
use Lucent\File\FileService;
|
||||||
use Lucent\File\ImageService;
|
|
||||||
use Lucent\Query\Query;
|
use Lucent\Query\Query;
|
||||||
use Lucent\Record\InputData\RecordInputData;
|
|
||||||
use Lucent\Record\RecordService;
|
use Lucent\Record\RecordService;
|
||||||
use Lucent\Record\Status;
|
|
||||||
use function Lucent\Response\fail;
|
use function Lucent\Response\fail;
|
||||||
use function Lucent\Response\ok;
|
use function Lucent\Response\ok;
|
||||||
|
|
||||||
@@ -27,20 +24,20 @@ class FileController extends Controller
|
|||||||
public function fromDisk(Request $request, string $disk)
|
public function fromDisk(Request $request, string $disk)
|
||||||
{
|
{
|
||||||
$imagePath = $request->route("any");
|
$imagePath = $request->route("any");
|
||||||
$disk = $this->fileService->loadDisk($disk);
|
$disk = $this->fileService->loadPublicDisk($disk);
|
||||||
return response()->file($disk->path($imagePath));
|
return response()->file($disk->path($imagePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function thumb(Request $request, string $disk)
|
public function thumb(Request $request, string $disk)
|
||||||
{
|
{
|
||||||
$imagePath = "thumbs/" . $request->route("any");
|
$imagePath = "thumbs/" . $request->route("any");
|
||||||
$disk = $this->fileService->loadDisk($disk);
|
$disk = $this->fileService->loadPublicDisk($disk);
|
||||||
return response()->file($disk->path($imagePath));
|
return response()->file($disk->path($imagePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function download(Request $request)
|
public function download(Request $request)
|
||||||
{
|
{
|
||||||
$disk = $this->fileService->loadDisk();
|
$disk = $this->fileService->loadPublicDisk();
|
||||||
return $disk->download($request->input("path"));
|
return $disk->download($request->input("path"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user