crazy stuff
This commit is contained in:
@@ -1,46 +0,0 @@
|
||||
<script>
|
||||
export let name;
|
||||
export let side = 48;
|
||||
const colors = [
|
||||
"#00AA55",
|
||||
"#009FD4",
|
||||
"#B381B3",
|
||||
"#939393",
|
||||
"#E3BC00",
|
||||
"#D47500",
|
||||
"#DC2A2A",
|
||||
"#3ede91",
|
||||
"#377dd4",
|
||||
"#0256b0",
|
||||
"#053d82",
|
||||
"#3d026e",
|
||||
"#b378e3",
|
||||
"#c4065c",
|
||||
"#543208",
|
||||
"#d97811",
|
||||
"#0c6b40",
|
||||
];
|
||||
let initials = "";
|
||||
if (name.split(" ").length > 1) {
|
||||
initials =
|
||||
name.split(" ")[0].charAt(0).toUpperCase() +
|
||||
name.split(" ")[1].charAt(0).toUpperCase();
|
||||
} else {
|
||||
initials =
|
||||
name.split(" ")[0].charAt(0).toUpperCase() +
|
||||
name.split(" ")[0].charAt(1).toUpperCase();
|
||||
}
|
||||
|
||||
let charIndex = name.charCodeAt(1) + name.length;
|
||||
let colorIndex = charIndex % 19;
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="avatar"
|
||||
title={name}
|
||||
style="background-color:{colors[
|
||||
colorIndex
|
||||
]}; height: {side}px;width: {side}px; font-size:{side / 2}px"
|
||||
>
|
||||
<div class="avatar__letters">{initials}</div>
|
||||
</div>
|
||||
@@ -1,7 +1,6 @@
|
||||
<script>
|
||||
import { createEventDispatcher } from "svelte";
|
||||
const dispatch = createEventDispatcher();
|
||||
import { range } from "lodash";
|
||||
import NavItem from "./NavItem.svelte";
|
||||
export let inModal;
|
||||
export let modalUrl;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
<script>
|
||||
import {createEventDispatcher, getContext} from "svelte";
|
||||
import {debounce} from "lodash";
|
||||
import {previewTitle} from "../../records/Preview";
|
||||
|
||||
import { createEventDispatcher, getContext } from "svelte";
|
||||
import { previewTitle } from "../../records/Preview";
|
||||
|
||||
const channel = getContext("channel");
|
||||
const dispatch = createEventDispatcher();
|
||||
@@ -10,9 +8,8 @@
|
||||
export let value = "";
|
||||
export let field;
|
||||
|
||||
let search = ""
|
||||
$: searchOptions = []
|
||||
|
||||
let search = "";
|
||||
$: searchOptions = [];
|
||||
|
||||
const updateResults = debounce((e) => {
|
||||
axios
|
||||
@@ -35,46 +32,36 @@
|
||||
|
||||
function apply(e, newOption) {
|
||||
e.preventDefault();
|
||||
value = newOption.id
|
||||
value = newOption.id;
|
||||
dispatch("addFilter");
|
||||
value = ""
|
||||
|
||||
value = "";
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<div class="reference-tags">
|
||||
<input
|
||||
type="search"
|
||||
on:keyup={updateResults}
|
||||
bind:value={search}
|
||||
placeholder={"Search for "+field.label}
|
||||
autocomplete="off"
|
||||
type="search"
|
||||
on:keyup={updateResults}
|
||||
bind:value={search}
|
||||
placeholder={"Search for " + field.label}
|
||||
autocomplete="off"
|
||||
/>
|
||||
|
||||
<div class="reference-tags-results">
|
||||
|
||||
{#if searchOptions}
|
||||
{#each searchOptions as option (option.id)}
|
||||
<div
|
||||
class="reference-tags-option"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
on:click={(e) => apply(e, option)}
|
||||
on:keypress={(e) => apply(e, option)}
|
||||
class="reference-tags-option"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
on:click={(e) => apply(e, option)}
|
||||
on:keypress={(e) => apply(e, option)}
|
||||
>
|
||||
{previewTitle(channel.schemas, option)}
|
||||
</div>
|
||||
|
||||
{:else}
|
||||
<div
|
||||
class="start-typing">
|
||||
Start typing...
|
||||
</div>
|
||||
<div class="start-typing">Start typing...</div>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
<script>
|
||||
|
||||
import {getContext, onMount} from "svelte";
|
||||
import RecordRow from "./RecordRow.svelte"
|
||||
|
||||
const channel = getContext("channel");
|
||||
let records = [];
|
||||
let graph = null;
|
||||
let users = [];
|
||||
onMount(() => {
|
||||
axios
|
||||
.get(channel.lucentUrl + "/home/records")
|
||||
.then((response) => {
|
||||
records = response.data.records;
|
||||
graph = response.data.graph;
|
||||
users = response.data.users;
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
<h3 class="header-small mb-4 mt-5">Latest Content changes</h3>
|
||||
{#if records.length > 0}
|
||||
|
||||
<div class="table">
|
||||
<table class="">
|
||||
<tbody>
|
||||
{#each records as record (record.id)}
|
||||
<tr>
|
||||
<RecordRow {graph} {record} {users}/>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/if}
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
<script>
|
||||
import {formatDistanceToNow, parseJSON} from "date-fns";
|
||||
import Avatar from "../account/Avatar.svelte";
|
||||
import {previewTitle} from "../records/Preview";
|
||||
import Preview from "../files/Preview.svelte";
|
||||
import {usernameById} from "../account/users";
|
||||
import {getContext} from "svelte";
|
||||
|
||||
const channel = getContext("channel");
|
||||
export let users;
|
||||
export let graph;
|
||||
export let record;
|
||||
let schema = channel.schemas.find((s) => s.name === record.schema);
|
||||
let frieldlyUpdatedAt = formatDistanceToNow(
|
||||
parseJSON(record._sys.updatedAt),
|
||||
{addSuffix: true}
|
||||
);
|
||||
</script>
|
||||
|
||||
<td>
|
||||
<div class="row-name">
|
||||
{#if record.status === "draft"}
|
||||
<span class="status">DRAFT</span>
|
||||
{/if}
|
||||
{#if schema.type === "files"}
|
||||
<Preview {record} size="tiny" showFilename={true}/>
|
||||
{:else}
|
||||
<a
|
||||
href="{channel.lucentUrl}/records/{record.id}"
|
||||
|
||||
>
|
||||
{previewTitle(channel.schemas, record, graph)}
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
</td>
|
||||
<td><a
|
||||
href="{channel.lucentUrl}/content/{schema.name}">{schema.label}</a
|
||||
>
|
||||
</td>
|
||||
|
||||
|
||||
<td>
|
||||
<div style="display: flex;gap: 14px">
|
||||
<Avatar name={usernameById(users, record._sys.updatedBy)} side={24}/>
|
||||
<div class="ms-2">
|
||||
{frieldlyUpdatedAt}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
@@ -1,34 +0,0 @@
|
||||
<script>
|
||||
import Avatar from "../account/Avatar.svelte";
|
||||
import {getContext} from "svelte";
|
||||
import Dropdown from "../common/Dropdown.svelte";
|
||||
|
||||
const channel = getContext("channel");
|
||||
const user = getContext("user");
|
||||
console.log( channel.commands)
|
||||
</script>
|
||||
|
||||
|
||||
<div class="top-nav ">
|
||||
<a class="top-nav-item" href="{channel.lucentUrl}/members">Members</a>
|
||||
|
||||
{#if channel.commands.length > 0}
|
||||
<Dropdown>
|
||||
<div slot="button">Actions</div>
|
||||
{#each channel.commands as command}
|
||||
<a href="{channel.lucentUrl}/command-report/{command.signature}" class="top-nav-item">{command.name}</a>
|
||||
{/each}
|
||||
</Dropdown>
|
||||
|
||||
{/if}
|
||||
<!-- <div>-->
|
||||
<!-- <form method="GET">-->
|
||||
<!-- <input type="search" name="filter[search_regex]" placeholder="Search"-->
|
||||
<!-- class="form-control" required/>-->
|
||||
<!-- </form>-->
|
||||
<!-- </div>-->
|
||||
<a href="{channel.lucentUrl}/profile">
|
||||
<Avatar side="28" name={user.name}/>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
<script>
|
||||
import NavbarMenu from "./NavbarMenu.svelte";
|
||||
import {getContext} from "svelte";
|
||||
|
||||
export let schema;
|
||||
const channel = getContext("channel");
|
||||
const readableSchemas = getContext("readableSchemas");
|
||||
|
||||
const fileSchemas = readableSchemas.filter((sc) => sc.type === "files");
|
||||
const otherSchemas = readableSchemas.filter((sc) => !sc.isEntry && sc.type === "collection");
|
||||
|
||||
</script>
|
||||
<div class="sidebar-top">
|
||||
<a class="logo" href="{channel.lucentUrl}">{channel.name}</a>
|
||||
<a class="nav-item" href="{channel.lucentUrl}/profile">
|
||||
</a>
|
||||
</div>
|
||||
<div class="sidebar">
|
||||
|
||||
|
||||
<NavbarMenu
|
||||
title="Content"
|
||||
schemas={ readableSchemas.filter((sc) => sc.isEntry)}
|
||||
schema={schema}
|
||||
expanded={true}
|
||||
/>
|
||||
|
||||
<NavbarMenu
|
||||
title="Files"
|
||||
schemas={ fileSchemas}
|
||||
schema={schema}
|
||||
/>
|
||||
|
||||
<NavbarMenu
|
||||
title="Other"
|
||||
schemas={ otherSchemas}
|
||||
schema={schema}
|
||||
/>
|
||||
</div>
|
||||
@@ -1,34 +0,0 @@
|
||||
<script>
|
||||
import {getContext} from "svelte";
|
||||
import Icon from "../common/Icon.svelte";
|
||||
|
||||
const channel = getContext("channel");
|
||||
export let schemas;
|
||||
export let title;
|
||||
export let schema;
|
||||
export let expanded = false;
|
||||
|
||||
if(schemas.find(s => s.name === schema?.name)){
|
||||
expanded = true;
|
||||
}
|
||||
|
||||
function toggleExpand(){
|
||||
expanded = !expanded;
|
||||
}
|
||||
</script>
|
||||
|
||||
<button class="sidebar-header" tabindex="0" on:click={toggleExpand}>
|
||||
{title}
|
||||
{#if expanded}
|
||||
<Icon icon="circle-chevron-up"></Icon>
|
||||
{:else}
|
||||
<Icon icon="circle-chevron-down"></Icon>
|
||||
{/if}
|
||||
</button>
|
||||
{#if expanded}
|
||||
{#each schemas as aschema}
|
||||
<a class="sidebar-item" class:active={aschema.name === schema?.name}
|
||||
aria-current="page"
|
||||
href="{channel.lucentUrl}/content/{aschema.name}">{aschema.label}</a>
|
||||
{/each}
|
||||
{/if}
|
||||
@@ -1,14 +1,13 @@
|
||||
<script>
|
||||
import {afterUpdate, getContext, onMount} from "svelte";
|
||||
import {isEqual} from "lodash";
|
||||
import { afterUpdate, getContext, onMount } from "svelte";
|
||||
import axios from "axios";
|
||||
import EditHeader from "./header/EditHeader.svelte"
|
||||
import FilePreview from "./FilePreview.svelte"
|
||||
import ContentTabs from "./header/ContentTabs.svelte"
|
||||
import FormField from "./FormField.svelte"
|
||||
import Graph from "./Graph.svelte"
|
||||
import Info from "./Info.svelte"
|
||||
import ErrorAlert from "../common/ErrorAlert.svelte"
|
||||
import EditHeader from "./header/EditHeader.svelte";
|
||||
import FilePreview from "./FilePreview.svelte";
|
||||
import ContentTabs from "./header/ContentTabs.svelte";
|
||||
import FormField from "./FormField.svelte";
|
||||
import Graph from "./Graph.svelte";
|
||||
import Info from "./Info.svelte";
|
||||
import ErrorAlert from "../common/ErrorAlert.svelte";
|
||||
import Title from "./header/Title.svelte";
|
||||
|
||||
const channel = getContext("channel");
|
||||
@@ -17,7 +16,7 @@
|
||||
export let record;
|
||||
export let graph = {
|
||||
records: [],
|
||||
edges: []
|
||||
edges: [],
|
||||
};
|
||||
// export let recordHistory;
|
||||
export let isCreateMode;
|
||||
@@ -29,14 +28,11 @@
|
||||
$: validationErrors = null;
|
||||
$: errorMessage = validationErrors
|
||||
? `Record submission failed. ${
|
||||
Object.entries(validationErrors).length
|
||||
} error(s)`
|
||||
Object.entries(validationErrors).length
|
||||
} error(s)`
|
||||
: null;
|
||||
|
||||
let activeFields = schema.fields.filter(
|
||||
(f) => f.name !== "id"
|
||||
);
|
||||
|
||||
let activeFields = schema.fields.filter((f) => f.name !== "id");
|
||||
|
||||
onMount(() => {
|
||||
setOriginalContent();
|
||||
@@ -104,7 +100,9 @@
|
||||
}
|
||||
|
||||
// remove trashed edges
|
||||
graph.edges = graph.edges?.filter((edge) => !edge._isTrashed && edge.source === record.id);
|
||||
graph.edges = graph.edges?.filter(
|
||||
(edge) => !edge._isTrashed && edge.source === record.id,
|
||||
);
|
||||
axios
|
||||
.post(channel.lucentUrl + "/records", {
|
||||
record: record,
|
||||
@@ -115,7 +113,8 @@
|
||||
console.log("SAVE: SAVED");
|
||||
|
||||
if (isCreateMode) {
|
||||
window.location = channel.lucentUrl + "/records/" + record.id;
|
||||
window.location =
|
||||
channel.lucentUrl + "/records/" + record.id;
|
||||
} else {
|
||||
record = response.data.records[0] ?? null;
|
||||
if (!record) {
|
||||
@@ -137,7 +136,7 @@
|
||||
errorMessage = error.response.data.error;
|
||||
} else {
|
||||
validationErrors = error.response.data.error;
|
||||
console.log(validationErrors)
|
||||
console.log(validationErrors);
|
||||
}
|
||||
}
|
||||
resolve(null);
|
||||
@@ -149,70 +148,61 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:beforeunload={beforeUnload}/>
|
||||
<svelte:window on:beforeunload={beforeUnload} />
|
||||
|
||||
<div class="record-edit">
|
||||
<div class="tools-header">
|
||||
<!-- <Manager managerRecords={recordHistory} {graph}/>-->
|
||||
<EditHeader {schema} bind:record {isCreateMode} bind:activeContentTab/>
|
||||
<EditHeader {schema} bind:record {isCreateMode} bind:activeContentTab />
|
||||
{#if isCreateMode}
|
||||
<button
|
||||
class="button primary btn-spinner"
|
||||
on:click={save}
|
||||
>
|
||||
<span
|
||||
class="spinner-border spinner-border-sm"
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<button class="button primary btn-spinner" on:click={save}>
|
||||
<span
|
||||
class="spinner-border spinner-border-sm"
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Create
|
||||
</button>
|
||||
{:else if hasUnsavedData}
|
||||
<button
|
||||
type="button"
|
||||
class="button primary ms-2 btn btn-primary btn-spinner"
|
||||
on:click={save}
|
||||
type="button"
|
||||
class="button primary ms-2 btn btn-primary btn-spinner"
|
||||
on:click={save}
|
||||
>
|
||||
<span
|
||||
class="spinner-border spinner-border-sm"
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span
|
||||
class="spinner-border spinner-border-sm"
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Save
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
<Title {schema} {record} {isCreateMode}/>
|
||||
<Title {schema} {record} {isCreateMode} />
|
||||
|
||||
|
||||
<ErrorAlert message={errorMessage}/>
|
||||
<ErrorAlert message={errorMessage} />
|
||||
|
||||
<div class=" mt-4" style="margin-bottom:150px;position:relative;">
|
||||
<ContentTabs
|
||||
{schema}
|
||||
{isCreateMode}
|
||||
bind:active={activeContentTab}
|
||||
/>
|
||||
<ContentTabs {schema} {isCreateMode} bind:active={activeContentTab} />
|
||||
{#if !["_graph", "_info"].includes(activeContentTab)}
|
||||
<FilePreview {record} {schema}/>
|
||||
<FilePreview {record} {schema} />
|
||||
{#each activeFields as field (field.name)}
|
||||
{#if activeContentTab === field.group}
|
||||
<FormField
|
||||
bind:data={record.data}
|
||||
bind:graph={graph}
|
||||
{field}
|
||||
{schema}
|
||||
{record}
|
||||
{validationErrors}
|
||||
{isCreateMode}
|
||||
bind:data={record.data}
|
||||
bind:graph
|
||||
{field}
|
||||
{schema}
|
||||
{record}
|
||||
{validationErrors}
|
||||
{isCreateMode}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
{:else if activeContentTab === "_graph"}
|
||||
<Graph {graph} {record}/>
|
||||
<Graph {graph} {record} />
|
||||
{:else if activeContentTab === "_info"}
|
||||
<Info {record} {graph} {users} {schema}/>
|
||||
<Info {record} {graph} {users} {schema} />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
<script>
|
||||
import {friendlyDate} from "../../helpers";
|
||||
import { friendlyDate } from "../../helpers";
|
||||
import Avatar from "../account/Avatar.svelte";
|
||||
import {usernameById} from "../account/users";
|
||||
import {isEqual} from "lodash";
|
||||
import { usernameById } from "../account/users";
|
||||
import Icon from "../common/Icon.svelte";
|
||||
import RevisionCell from "./revisions/RevisionCell.svelte";
|
||||
import {getContext} from "svelte";
|
||||
import { getContext } from "svelte";
|
||||
import RevisionEdgeRow from "./revisions/RevisionEdgeRow.svelte";
|
||||
|
||||
const channel = getContext("channel");
|
||||
@@ -30,27 +29,27 @@
|
||||
});
|
||||
|
||||
function getEdgesByField(fieldsWithDiff, revision) {
|
||||
|
||||
edgeFieldsDiff = graph.edges.filter((e) => e.depth === 1).reduce((c, e) => {
|
||||
if (!c[e.field]) {
|
||||
c[e.field] = {
|
||||
record: [],
|
||||
revision: [],
|
||||
edgeFieldsDiff = graph.edges
|
||||
.filter((e) => e.depth === 1)
|
||||
.reduce((c, e) => {
|
||||
if (!c[e.field]) {
|
||||
c[e.field] = {
|
||||
record: [],
|
||||
revision: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
c[e.field]["record"].push(e)
|
||||
return c;
|
||||
}, {});
|
||||
|
||||
c[e.field]["record"].push(e);
|
||||
return c;
|
||||
}, {});
|
||||
|
||||
edgeFieldsDiff = revision._edges.reduce((c, e) => {
|
||||
if (!c[e.field]) {
|
||||
c[e.field] = {
|
||||
record: [],
|
||||
revision: [],
|
||||
}
|
||||
};
|
||||
}
|
||||
c[e.field]["revision"].push(e)
|
||||
c[e.field]["revision"].push(e);
|
||||
return c;
|
||||
}, edgeFieldsDiff);
|
||||
}
|
||||
@@ -62,7 +61,7 @@
|
||||
fieldsWithDiff = schema.fields.filter((f) => {
|
||||
return !isEqual(selectedRevision.data[f.name], record.data[f.name]);
|
||||
});
|
||||
getEdgesByField(fieldsWithDiff, revision)
|
||||
getEdgesByField(fieldsWithDiff, revision);
|
||||
revisionSection.scrollIntoView();
|
||||
}
|
||||
|
||||
@@ -71,7 +70,7 @@
|
||||
rollbackError = "";
|
||||
axios
|
||||
.post(
|
||||
`${channel.lucentUrl}/records/${record.id}/rollback/${selectedRevision._sys.version}`
|
||||
`${channel.lucentUrl}/records/${record.id}/rollback/${selectedRevision._sys.version}`,
|
||||
)
|
||||
.then((response) => {
|
||||
window.location.reload();
|
||||
@@ -84,7 +83,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="lx-card ">
|
||||
<div class="lx-card">
|
||||
<div class="row">
|
||||
<div class="col-8">
|
||||
<div>
|
||||
@@ -98,24 +97,22 @@
|
||||
<div>
|
||||
<span class="label text-end text-muted"> created </span>
|
||||
<Avatar
|
||||
name={usernameById(users, record._sys.createdBy)}
|
||||
side={24}
|
||||
name={usernameById(users, record._sys.createdBy)}
|
||||
side={24}
|
||||
/>
|
||||
{friendlyDate(record._sys.createdAt)}
|
||||
</div>
|
||||
<div>
|
||||
<span class="label text-end text-muted">updated </span>
|
||||
<span class="label text-end text-muted">updated </span>
|
||||
<Avatar
|
||||
name={usernameById(users, record._sys.updatedBy)}
|
||||
side={24}
|
||||
name={usernameById(users, record._sys.updatedBy)}
|
||||
side={24}
|
||||
/>
|
||||
{friendlyDate(record._sys.updatedAt)}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<span class="label d-block text-muted "
|
||||
>Rules for this schema
|
||||
</span>
|
||||
<span class="label d-block text-muted">Rules for this schema </span>
|
||||
<small>
|
||||
Each record maintains the last {schema.revisions}
|
||||
versions
|
||||
@@ -125,33 +122,31 @@
|
||||
</div>
|
||||
<div class="revisions">
|
||||
{#if schema.revisions > 0}
|
||||
<div class="header-small mb-3">Revisions</div>
|
||||
<div class="header-small mb-3">Revisions</div>
|
||||
{#each revisions as revision}
|
||||
{#if revision._sys.version !== record._sys.version}
|
||||
<div
|
||||
class="revision"
|
||||
class:active={revision._sys.version ===
|
||||
class="revision"
|
||||
class:active={revision._sys.version ===
|
||||
selectedRevision?._sys.version}
|
||||
>
|
||||
|
||||
<div class="version">
|
||||
<span>version {revision._sys.version}</span>
|
||||
<Avatar
|
||||
name={usernameById(users, revision._sys.updatedBy)}
|
||||
side={24}
|
||||
name={usernameById(users, revision._sys.updatedBy)}
|
||||
side={24}
|
||||
/>
|
||||
{friendlyDate(revision._sys.updatedAt)}
|
||||
</div>
|
||||
|
||||
<div class="col-3 text-center">
|
||||
<button
|
||||
disabled={revision._sys.version ===
|
||||
disabled={revision._sys.version ===
|
||||
selectedRevision?._sys.version}
|
||||
class="button"
|
||||
on:click={(e) => compare(e, revision)}
|
||||
>Compare
|
||||
</button
|
||||
>
|
||||
class="button"
|
||||
on:click={(e) => compare(e, revision)}
|
||||
>Compare
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -169,15 +164,13 @@
|
||||
<p class="text-center fw-bold mb-3 mt-5">
|
||||
If you choose to rollback to this revision
|
||||
</p>
|
||||
<button
|
||||
on:click={rollback}
|
||||
class="button"
|
||||
>
|
||||
<button on:click={rollback} class="button">
|
||||
Rollback to version {selectedRevision._sys.version}
|
||||
</button>
|
||||
|
||||
{#if rollbackError}
|
||||
<span class="d-block text-danger mt-3">{rollbackError}</span>
|
||||
<span class="d-block text-danger mt-3">{rollbackError}</span
|
||||
>
|
||||
{/if}
|
||||
<div class="mt-3">
|
||||
{#each fieldsWithDiff as field}
|
||||
@@ -188,31 +181,28 @@
|
||||
<!-- <div class="d-block" style="width:200px;">
|
||||
{field.label}
|
||||
</div> -->
|
||||
<div
|
||||
class="revision-field"
|
||||
style="overflow:hidden"
|
||||
>
|
||||
<div class="revision-field" style="overflow:hidden">
|
||||
<div class="compare-left">
|
||||
<RevisionCell
|
||||
{field}
|
||||
side={record.data[field.name]}
|
||||
colorClass="text-danger"
|
||||
{field}
|
||||
side={record.data[field.name]}
|
||||
colorClass="text-danger"
|
||||
/>
|
||||
</div>
|
||||
<div class="compare-center">
|
||||
<span class="me-1">{field.label}</span>
|
||||
<Icon
|
||||
icon="angle-right"
|
||||
width="12"
|
||||
height="12"
|
||||
icon="angle-right"
|
||||
width="12"
|
||||
height="12"
|
||||
/>
|
||||
</div>
|
||||
<div class="compare-right">
|
||||
<RevisionCell
|
||||
edges={selectedRevision._edges}
|
||||
{field}
|
||||
side={selectedRevision.data[field.name]}
|
||||
colorClass="text-success"
|
||||
edges={selectedRevision._edges}
|
||||
{field}
|
||||
side={selectedRevision.data[field.name]}
|
||||
colorClass="text-success"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -226,22 +216,16 @@
|
||||
{/if}
|
||||
|
||||
<div class="mt-3">
|
||||
<p class="text-center fw-bold mb-3 mt-5">
|
||||
Record References
|
||||
</p>
|
||||
<p class="text-center fw-bold mb-3 mt-5">Record References</p>
|
||||
{#each Object.entries(edgeFieldsDiff) as [field, edges]}
|
||||
<div
|
||||
class="revision-references"
|
||||
style="overflow:hidden"
|
||||
>
|
||||
<div class="revision-references" style="overflow:hidden">
|
||||
<div class="reference-field">
|
||||
{field}:
|
||||
</div>
|
||||
<div class="reference-compare">
|
||||
|
||||
<p class="">Record</p>
|
||||
{#each edges.record as edge}
|
||||
<RevisionEdgeRow {edge}/>
|
||||
<RevisionEdgeRow {edge} />
|
||||
{:else}
|
||||
<p>No references</p>
|
||||
{/each}
|
||||
@@ -249,7 +233,7 @@
|
||||
<div class="reference-compare">
|
||||
<p class="text-success">Revision</p>
|
||||
{#each edges.revision as edge}
|
||||
<RevisionEdgeRow {edge}/>
|
||||
<RevisionEdgeRow {edge} />
|
||||
{:else}
|
||||
<p>No references</p>
|
||||
{/each}
|
||||
@@ -258,7 +242,5 @@
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
<script>
|
||||
import {afterUpdate, createEventDispatcher, getContext, onMount} from "svelte";
|
||||
import {
|
||||
afterUpdate,
|
||||
createEventDispatcher,
|
||||
getContext,
|
||||
onMount,
|
||||
} from "svelte";
|
||||
|
||||
import {isEqual} from "lodash";
|
||||
import FormField from "./FormField.svelte";
|
||||
import FilePreview from "./FilePreview.svelte";
|
||||
import ContentTabs from "./header/ContentTabs.svelte";
|
||||
@@ -16,7 +20,7 @@
|
||||
export let record;
|
||||
export let graph = {
|
||||
records: [],
|
||||
edges: []
|
||||
edges: [],
|
||||
};
|
||||
export let isCreateMode;
|
||||
let originalContent;
|
||||
@@ -25,13 +29,11 @@
|
||||
$: validationErrors = null;
|
||||
$: errorMessage = validationErrors
|
||||
? `Record submission failed. ${
|
||||
Object.entries(validationErrors).length
|
||||
} error(s)`
|
||||
Object.entries(validationErrors).length
|
||||
} error(s)`
|
||||
: null;
|
||||
|
||||
let activeFields = schema.fields.filter(
|
||||
(f) => f.name !== "id"
|
||||
);
|
||||
let activeFields = schema.fields.filter((f) => f.name !== "id");
|
||||
|
||||
let tabname = "_default";
|
||||
let fieldToTabs = schema.fields.reduce((c, f) => {
|
||||
@@ -114,8 +116,10 @@
|
||||
return;
|
||||
}
|
||||
// remove trashed edges
|
||||
graph.edges = graph.edges?.filter((edge) => !edge._isTrashed && edge.source === record.id) ?? [];
|
||||
|
||||
graph.edges =
|
||||
graph.edges?.filter(
|
||||
(edge) => !edge._isTrashed && edge.source === record.id,
|
||||
) ?? [];
|
||||
|
||||
axios
|
||||
.post(channel.lucentUrl + "/records", {
|
||||
@@ -150,64 +154,55 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:beforeunload={beforeUnload}/>
|
||||
<svelte:window on:beforeunload={beforeUnload} />
|
||||
|
||||
<div class="inline-edit record-edit">
|
||||
<div class="tools-header">
|
||||
<EditHeader {schema} bind:record {isCreateMode} bind:activeContentTab/>
|
||||
<EditHeader {schema} bind:record {isCreateMode} bind:activeContentTab />
|
||||
{#if isCreateMode}
|
||||
<button
|
||||
class="button primary btn-spinner"
|
||||
on:click={save}
|
||||
>
|
||||
<span
|
||||
class="spinner-border spinner-border-sm"
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<button class="button primary btn-spinner" on:click={save}>
|
||||
<span
|
||||
class="spinner-border spinner-border-sm"
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Create
|
||||
</button>
|
||||
{:else if hasUnsavedData}
|
||||
<button
|
||||
type="button"
|
||||
class="button primary ms-2 btn btn-primary btn-spinner"
|
||||
on:click={save}
|
||||
type="button"
|
||||
class="button primary ms-2 btn btn-primary btn-spinner"
|
||||
on:click={save}
|
||||
>
|
||||
<span
|
||||
class="spinner-border spinner-border-sm"
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span
|
||||
class="spinner-border spinner-border-sm"
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Save
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
<Title {schema} {record} {isCreateMode}/>
|
||||
<ErrorAlert message={errorMessage}/>
|
||||
<Title {schema} {record} {isCreateMode} />
|
||||
<ErrorAlert message={errorMessage} />
|
||||
|
||||
<div class=" mt-4" style="margin-bottom:150px;position:relative;">
|
||||
<ContentTabs
|
||||
{schema}
|
||||
{isCreateMode}
|
||||
bind:active={activeContentTab}
|
||||
/>
|
||||
<FilePreview {record} {schema}/>
|
||||
<ContentTabs {schema} {isCreateMode} bind:active={activeContentTab} />
|
||||
<FilePreview {record} {schema} />
|
||||
<!-- <fieldset disabled="disabled"> -->
|
||||
{#each activeFields as field (field.name)}
|
||||
{#if activeContentTab === field.group}
|
||||
<FormField
|
||||
bind:data={record.data}
|
||||
bind:graph={graph}
|
||||
{field}
|
||||
{schema}
|
||||
{record}
|
||||
{validationErrors}
|
||||
{isCreateMode}
|
||||
bind:data={record.data}
|
||||
bind:graph
|
||||
{field}
|
||||
{schema}
|
||||
{record}
|
||||
{validationErrors}
|
||||
{isCreateMode}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
<!-- </fieldset> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
<script>
|
||||
import {getContext} from "svelte";
|
||||
import {debounce} from "lodash";
|
||||
import {previewTitle} from "../Preview";
|
||||
import {getErrorMessage} from "./errorMessage";
|
||||
import {insertEdges} from "./reference.js";
|
||||
import { getContext } from "svelte";
|
||||
import { previewTitle } from "../Preview";
|
||||
import { getErrorMessage } from "./errorMessage";
|
||||
import { insertEdges } from "./reference.js";
|
||||
import Icon from "../../common/Icon.svelte";
|
||||
|
||||
const channel = getContext("channel");
|
||||
@@ -15,20 +14,24 @@
|
||||
export let validationErrors;
|
||||
$: errorMessage = getErrorMessage(validationErrors, field.name);
|
||||
|
||||
$: references = graph.edges
|
||||
.filter((edge) => edge.field === field.name)
|
||||
.map((edge) => {
|
||||
return graph.records.find((increc) => increc.id == edge.target && record.id == edge.source);
|
||||
}).filter((rec) => (rec?.id ? true : false)) ?? [];
|
||||
|
||||
let search = ""
|
||||
$: searchOptions = []
|
||||
$: references =
|
||||
graph.edges
|
||||
.filter((edge) => edge.field === field.name)
|
||||
.map((edge) => {
|
||||
return graph.records.find(
|
||||
(increc) =>
|
||||
increc.id == edge.target && record.id == edge.source,
|
||||
);
|
||||
})
|
||||
.filter((rec) => (rec?.id ? true : false)) ?? [];
|
||||
|
||||
let search = "";
|
||||
$: searchOptions = [];
|
||||
|
||||
function removeReference(e, recordId) {
|
||||
e.preventDefault();
|
||||
graph.edges = graph.edges.filter(
|
||||
(edge) => !(edge.target === recordId && edge.field === field.name)
|
||||
(edge) => !(edge.target === recordId && edge.field === field.name),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -41,14 +44,14 @@
|
||||
schema: field.collections[0],
|
||||
status: "published",
|
||||
data: {
|
||||
[field.searchField]: newValue
|
||||
}
|
||||
[field.searchField]: newValue,
|
||||
},
|
||||
},
|
||||
})
|
||||
.then((response) => {
|
||||
searchOptions = [];
|
||||
insert(e, response.data.records[0]);
|
||||
console.log(response)
|
||||
console.log(response);
|
||||
})
|
||||
.catch((error) => {
|
||||
searchOptions = [];
|
||||
@@ -58,10 +61,16 @@
|
||||
|
||||
function insert(e, insertRecord) {
|
||||
e.preventDefault();
|
||||
graph = insertEdges(graph, record, [insertRecord], field.name, e.detail.action);
|
||||
search = ""
|
||||
searchEl.focus()
|
||||
searchEl.blur()
|
||||
graph = insertEdges(
|
||||
graph,
|
||||
record,
|
||||
[insertRecord],
|
||||
field.name,
|
||||
e.detail.action,
|
||||
);
|
||||
search = "";
|
||||
searchEl.focus();
|
||||
searchEl.blur();
|
||||
}
|
||||
|
||||
const updateResults = debounce((e) => {
|
||||
@@ -82,9 +91,8 @@
|
||||
console.log(error);
|
||||
});
|
||||
}, 500);
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<div class="reference-tags">
|
||||
{#if errorMessage}
|
||||
<div class="invalid-feedback d-block mb-3">
|
||||
@@ -93,44 +101,39 @@
|
||||
{/if}
|
||||
|
||||
<input
|
||||
type="search"
|
||||
bind:this={searchEl}
|
||||
{id}
|
||||
on:keyup={updateResults}
|
||||
class:is-invalid={errorMessage}
|
||||
bind:value={search}
|
||||
placeholder={"Search for "+field.label}
|
||||
autocomplete="off"
|
||||
type="search"
|
||||
bind:this={searchEl}
|
||||
{id}
|
||||
on:keyup={updateResults}
|
||||
class:is-invalid={errorMessage}
|
||||
bind:value={search}
|
||||
placeholder={"Search for " + field.label}
|
||||
autocomplete="off"
|
||||
/>
|
||||
|
||||
<div class="reference-tags-results">
|
||||
|
||||
{#if searchOptions}
|
||||
{#each searchOptions as option (option.id)}
|
||||
<div
|
||||
class="reference-tags-option"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
on:click={(e) => insert(e, option)}
|
||||
on:keypress={(e) => insert(e, option)}
|
||||
>
|
||||
{previewTitle(channel.schemas, option ,graph)}
|
||||
</div>
|
||||
|
||||
{:else}
|
||||
<div
|
||||
class="start-typing">
|
||||
Start typing...
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
{#if search }
|
||||
<div
|
||||
class="reference-tags-option"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
on:click={(e) => saveNew(e,search)}
|
||||
on:keypress={(e) => saveNew(e,search)}
|
||||
on:click={(e) => insert(e, option)}
|
||||
on:keypress={(e) => insert(e, option)}
|
||||
>
|
||||
{previewTitle(channel.schemas, option, graph)}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="start-typing">Start typing...</div>
|
||||
{/each}
|
||||
{/if}
|
||||
{#if search}
|
||||
<div
|
||||
class="reference-tags-option"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
on:click={(e) => saveNew(e, search)}
|
||||
on:keypress={(e) => saveNew(e, search)}
|
||||
>
|
||||
Add "{search}"
|
||||
</div>
|
||||
@@ -142,26 +145,23 @@
|
||||
<div style="display: flex;align-items: center;gap: 4px">
|
||||
{#each references as record (record.id)}
|
||||
<span class="reference-tags-selected-value">
|
||||
<a
|
||||
class="record-title"
|
||||
href="{channel.lucentUrl}/records/{record.id}"
|
||||
>
|
||||
{previewTitle(channel.schemas, record)}
|
||||
</a>
|
||||
<a
|
||||
class="record-title"
|
||||
href="{channel.lucentUrl}/records/{record.id}"
|
||||
>
|
||||
{previewTitle(channel.schemas, record)}
|
||||
</a>
|
||||
|
||||
<button
|
||||
on:click|preventDefault={(e) => removeReference(e, record.id)}
|
||||
type="button"
|
||||
class="button-text"
|
||||
aria-label="Close"
|
||||
on:click|preventDefault={(e) =>
|
||||
removeReference(e, record.id)}
|
||||
type="button"
|
||||
class="button-text"
|
||||
aria-label="Close"
|
||||
>
|
||||
<Icon width={12} height={12} icon="close"></Icon>
|
||||
</button>
|
||||
|
||||
<Icon width={12} height={12} icon="close"></Icon>
|
||||
</button>
|
||||
</span>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script>
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { getContext } from "svelte";
|
||||
import Icon from "../../common/Icon.svelte";
|
||||
import { getErrorMessage } from "./errorMessage";
|
||||
@@ -14,12 +13,11 @@
|
||||
|
||||
function generateId(e) {
|
||||
e.preventDefault();
|
||||
value = uuidv4();
|
||||
value = randomUUID();
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="mb-0">
|
||||
|
||||
<div class="d-flex justify-content-between">
|
||||
<input
|
||||
type="text"
|
||||
@@ -31,13 +29,13 @@
|
||||
{readonly}
|
||||
/>
|
||||
{#if !readonly}
|
||||
<button
|
||||
class="btn btn-primary ms-2"
|
||||
title="Generate a new UUIDv4"
|
||||
on:click={generateId}
|
||||
>
|
||||
<Icon icon="dice" />
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-primary ms-2"
|
||||
title="Generate a new UUIDv4"
|
||||
on:click={generateId}
|
||||
>
|
||||
<Icon icon="dice" />
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
<script>
|
||||
import { uniqueId } from "lodash";
|
||||
import { getContext } from "svelte";
|
||||
const channelurl = getContext("channelurl");
|
||||
export let field;
|
||||
export let value;
|
||||
export let schema;
|
||||
let id = uniqueId();
|
||||
let id = randomUUID();
|
||||
</script>
|
||||
|
||||
<div class="mb-0">
|
||||
@@ -25,6 +24,6 @@
|
||||
placeholder="https://www.example.com"
|
||||
/>
|
||||
{#if field.help}
|
||||
<small class=" text-primary opacity-50">{field.help}</small>
|
||||
<small class=" text-primary opacity-50">{field.help}</small>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -1,24 +1,31 @@
|
||||
import {uniqBy} from "lodash";
|
||||
export function insertEdges(
|
||||
graph,
|
||||
sourceRecord,
|
||||
targetRecords,
|
||||
fieldName,
|
||||
action = "",
|
||||
) {
|
||||
let newEdges = targetRecords.map((r) => {
|
||||
return {
|
||||
target: r.id,
|
||||
source: sourceRecord.id,
|
||||
sourceSchema: sourceRecord.schema,
|
||||
targetSchema: r.schema,
|
||||
field: fieldName,
|
||||
depth: 1,
|
||||
rank: "",
|
||||
};
|
||||
});
|
||||
|
||||
export function insertEdges(graph, sourceRecord, targetRecords, fieldName, action = "") {
|
||||
let newEdges = targetRecords.map((r) => {
|
||||
return {
|
||||
target: r.id,
|
||||
source: sourceRecord.id,
|
||||
sourceSchema: sourceRecord.schema,
|
||||
targetSchema: r.schema,
|
||||
field: fieldName,
|
||||
depth: 1,
|
||||
rank: ""
|
||||
};
|
||||
});
|
||||
let replacedEdges = graph.edges;
|
||||
if (action === "replace") {
|
||||
replacedEdges = replacedEdges.filter((edge) => edge.field !== field.name);
|
||||
}
|
||||
|
||||
let replacedEdges = graph.edges;
|
||||
if (action === "replace") {
|
||||
replacedEdges = replacedEdges.filter((edge) => edge.field !== field.name);
|
||||
}
|
||||
|
||||
graph.records = uniqBy([...graph.records, ...targetRecords], (r) => r.id);
|
||||
graph.edges = uniqBy([...replacedEdges, ...newEdges], (edge) => edge.source + edge.target + edge.field + edge.depth);
|
||||
return graph;
|
||||
graph.records = uniqBy([...graph.records, ...targetRecords], (r) => r.id);
|
||||
graph.edges = uniqBy(
|
||||
[...replacedEdges, ...newEdges],
|
||||
(edge) => edge.source + edge.target + edge.field + edge.depth,
|
||||
);
|
||||
return graph;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user