singleton and embed records
This commit is contained in:
@@ -124,10 +124,17 @@
|
|||||||
"italic": {
|
"italic": {
|
||||||
path: '<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m8.874 19 6.143-14M6 19h6.33m-.66-14H18"/>',
|
path: '<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m8.874 19 6.143-14M6 19h6.33m-.66-14H18"/>',
|
||||||
viewBox: "0 0 24 24",
|
viewBox: "0 0 24 24",
|
||||||
|
},
|
||||||
|
"undo": {
|
||||||
|
path: '<path fill-rule="evenodd" clip-rule="evenodd" d="M7.53033 3.46967C7.82322 3.76256 7.82322 4.23744 7.53033 4.53033L5.81066 6.25H15C18.1756 6.25 20.75 8.82436 20.75 12C20.75 15.1756 18.1756 17.75 15 17.75H8.00001C7.58579 17.75 7.25001 17.4142 7.25001 17C7.25001 16.5858 7.58579 16.25 8.00001 16.25H15C17.3472 16.25 19.25 14.3472 19.25 12C19.25 9.65279 17.3472 7.75 15 7.75H5.81066L7.53033 9.46967C7.82322 9.76256 7.82322 10.2374 7.53033 10.5303C7.23744 10.8232 6.76256 10.8232 6.46967 10.5303L3.46967 7.53033C3.17678 7.23744 3.17678 6.76256 3.46967 6.46967L6.46967 3.46967C6.76256 3.17678 7.23744 3.17678 7.53033 3.46967Z" fill="#1C274C"/>',
|
||||||
|
viewBox: "0 0 24 24",
|
||||||
|
},
|
||||||
|
"destroy": {
|
||||||
|
path: '<path d="M17 7L15 9" stroke="#1C274C" stroke-width="1.5" stroke-linecap="round"/><path d="M19.5 7.5L20.5 8" stroke="#1C274C" stroke-width="1.5" stroke-linecap="round"/><path d="M16 3.5L16.5 4.5" stroke="#1C274C" stroke-width="1.5" stroke-linecap="round"/><path d="M19 5L20 4" stroke="#1C274C" stroke-width="1.5" stroke-linecap="round"/><path\n'+' d="M5.75 8.00337C6.85315 7.36523 8.13392 7 9.5 7C13.6421 7 17 10.3579 17 14.5C17 18.6421 13.6421 22 9.5 22C5.35786 22 2 18.6421 2 14.5C2 13.1339 2.36523 11.8532 3.00337 10.75" stroke="#1C274C" stroke-width="1.5" stroke-linecap="round"/>',
|
||||||
|
viewBox: "0 0 24 24",
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export let width = 16;
|
export let width = 16;
|
||||||
export let height = 16;
|
export let height = 16;
|
||||||
export let icon = "";
|
export let icon = "";
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import {getContext} from "svelte";
|
import {getContext} from "svelte";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
const channel = getContext("channel");
|
const channel = getContext("channel");
|
||||||
export let selected;
|
export let selected;
|
||||||
@@ -24,7 +25,7 @@
|
|||||||
axios
|
axios
|
||||||
.post(channel.lucentUrl + "/records/status/" + status, {
|
.post(channel.lucentUrl + "/records/status/" + status, {
|
||||||
schemaName: schema.name,
|
schemaName: schema.name,
|
||||||
records: selected
|
records: selected.map((s) => s.id),
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
@@ -50,20 +51,6 @@
|
|||||||
</button
|
</button
|
||||||
>
|
>
|
||||||
{#if filter["status_in"] === "trashed"}
|
{#if filter["status_in"] === "trashed"}
|
||||||
<button
|
|
||||||
on:click|preventDefault={(e) => changeStatus(e, "published")}
|
|
||||||
type="button"
|
|
||||||
class="button">Publish
|
|
||||||
</button
|
|
||||||
>
|
|
||||||
{#if schema.hasDrafts}
|
|
||||||
<button
|
|
||||||
on:click|preventDefault={(e) => changeStatus(e, "draft")}
|
|
||||||
type="button"
|
|
||||||
class="button">Make Draft
|
|
||||||
</button
|
|
||||||
>
|
|
||||||
{/if}
|
|
||||||
<button
|
<button
|
||||||
on:click|preventDefault={deleteRecords}
|
on:click|preventDefault={deleteRecords}
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ export function sortByField(from, to, edges, fieldName, references) {
|
|||||||
if (from === to) {
|
if (from === to) {
|
||||||
return edges;
|
return edges;
|
||||||
}
|
}
|
||||||
let referenceIds = references.map(r => r.id);
|
let referenceIds = references.map(r => r.record.id);
|
||||||
let edgesTosort = edges?.filter((ed) => ed.field === fieldName && ed.depth === 1 && referenceIds.includes(ed.target)) ?? [];
|
let edgesTosort = edges?.filter((ed) => ed.field === fieldName && ed.depth === 1 && referenceIds.includes(ed.target)) ?? [];
|
||||||
let remainingEdge = edges?.filter((ed) => !(ed.field === fieldName && ed.depth === 1)) ?? [];
|
let remainingEdge = edges?.filter((ed) => !(ed.field === fieldName && ed.depth === 1)) ?? [];
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script>
|
<script>
|
||||||
import {createEventDispatcher, getContext, onMount} from "svelte";
|
import {getContext} from "svelte";
|
||||||
import Icon from "../common/Icon.svelte";
|
import Icon from "../common/Icon.svelte";
|
||||||
import Folder from "./Folder.svelte";
|
import Folder from "./Folder.svelte";
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
<div class="sidebar-folder">
|
<div class="sidebar-folder">
|
||||||
{#if folder.name !== ""}
|
{#if folder.name !== ""}
|
||||||
<button class="sidebar-header" tabindex="0" on:click={toggleExpand}>
|
<button class="sidebar-header" tabindex="0" on:click={toggleExpand}>
|
||||||
{folder.name.replaceAll("_"," ") ?? "Main"}
|
{folder.name.replaceAll("_", " ") ?? "Main"}
|
||||||
{#if expanded}
|
{#if expanded}
|
||||||
<Icon icon="circle-chevron-up"></Icon>
|
<Icon icon="circle-chevron-up"></Icon>
|
||||||
{:else}
|
{:else}
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
{#if expanded}
|
{#if expanded}
|
||||||
{#each folder.folders as aFolder}
|
{#each folder.folders as aFolder}
|
||||||
<Folder folder={aFolder} schema={schema} ></Folder>
|
<Folder folder={aFolder} schema={schema}></Folder>
|
||||||
{/each}
|
{/each}
|
||||||
|
|
||||||
{#each folder.files as aSchema}
|
{#each folder.files as aSchema}
|
||||||
|
|||||||
@@ -21,12 +21,7 @@
|
|||||||
</Dropdown>
|
</Dropdown>
|
||||||
|
|
||||||
{/if}
|
{/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">
|
<a href="{channel.lucentUrl}/profile">
|
||||||
<Avatar side="28" name={user.name}/>
|
<Avatar side="28" name={user.name}/>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -31,10 +31,6 @@
|
|||||||
carry = addToFolder(carry, schema.folder,schema)
|
carry = addToFolder(carry, schema.folder,schema)
|
||||||
return carry;
|
return carry;
|
||||||
}, {name: "", files: [], folders: [], shoudlExpand:true});
|
}, {name: "", files: [], folders: [], shoudlExpand:true});
|
||||||
|
|
||||||
// const fileSchemas = readableSchemas.filter((sc) => sc.type === "files");
|
|
||||||
// const otherSchemas = readableSchemas.filter((sc) => !sc.isEntry && sc.type === "collection");
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<div class="sidebar-top">
|
<div class="sidebar-top">
|
||||||
<a class="logo" href="{channel.lucentUrl}">{channel.name}</a>
|
<a class="logo" href="{channel.lucentUrl}">{channel.name}</a>
|
||||||
@@ -42,27 +38,5 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="sidebar">
|
<div class="sidebar">
|
||||||
|
|
||||||
|
|
||||||
<Folder folder={schemaTree} {schema} ></Folder>
|
<Folder folder={schemaTree} {schema} ></Folder>
|
||||||
|
|
||||||
|
|
||||||
<!-- <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>
|
</div>
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
<script>
|
|
||||||
import {getContext} from "svelte";
|
|
||||||
import Icon from "../common/Icon.svelte";
|
|
||||||
|
|
||||||
const channel = getContext("channel");
|
|
||||||
export let folder;
|
|
||||||
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}
|
|
||||||
|
|
||||||
{#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}
|
|
||||||
@@ -4,7 +4,13 @@
|
|||||||
import PreviewFile from "../previews/PreviewFile.svelte";
|
import PreviewFile from "../previews/PreviewFile.svelte";
|
||||||
import Dropdown from "../../common/Dropdown.svelte";
|
import Dropdown from "../../common/Dropdown.svelte";
|
||||||
import Dialog from "../../dialog/Dialog.svelte";
|
import Dialog from "../../dialog/Dialog.svelte";
|
||||||
import {insertEdges} from "./reference.js";
|
import {
|
||||||
|
fullDeleteRecord,
|
||||||
|
graphToReferences,
|
||||||
|
insertEdges,
|
||||||
|
removeReferenceFromGraph,
|
||||||
|
restoreReferenceToGraph
|
||||||
|
} from "./reference.js";
|
||||||
import {getContext} from "svelte";
|
import {getContext} from "svelte";
|
||||||
|
|
||||||
const channel = getContext("channel");
|
const channel = getContext("channel");
|
||||||
@@ -12,11 +18,7 @@
|
|||||||
export let record;
|
export let record;
|
||||||
export let graph
|
export let graph
|
||||||
let browseModal;
|
let browseModal;
|
||||||
$: references = graph?.edges
|
$: references = graphToReferences(graph, record, field)
|
||||||
.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 collections = channel.schemas.filter((aschema) =>
|
let collections = channel.schemas.filter((aschema) =>
|
||||||
field.collections.includes(aschema.name)
|
field.collections.includes(aschema.name)
|
||||||
@@ -24,9 +26,17 @@
|
|||||||
|
|
||||||
function removeReference(e) {
|
function removeReference(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
graph.edges = graph.edges.filter(
|
graph.edges = removeReferenceFromGraph(graph, field, e.detail)
|
||||||
(edge) => !(edge.target === e.detail && edge.field === field.name)
|
}
|
||||||
);
|
|
||||||
|
function restoreReference(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
graph.edges = restoreReferenceToGraph(graph, field, e.detail)
|
||||||
|
}
|
||||||
|
|
||||||
|
function fullDelete(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
graph.edges = fullDeleteRecord(channel,graph, field, e.detail)
|
||||||
}
|
}
|
||||||
|
|
||||||
function openBrowseModal(e, schema) {
|
function openBrowseModal(e, schema) {
|
||||||
@@ -73,10 +83,17 @@
|
|||||||
</div>
|
</div>
|
||||||
{#if references.length > 0}
|
{#if references.length > 0}
|
||||||
<Sortable sortableClass="mt-3" on:update={reorder}>
|
<Sortable sortableClass="mt-3" on:update={reorder}>
|
||||||
{#each references as reference (reference.id)}
|
{#each references as reference (reference.record.id)}
|
||||||
<!--This div helps the sorting thing-->
|
<!--This div helps the sorting thing-->
|
||||||
<div>
|
<div>
|
||||||
<PreviewFile record={reference} hasDelete={true} on:remove={removeReference}></PreviewFile>
|
<PreviewFile
|
||||||
|
record={reference.record}
|
||||||
|
edge={reference.edge}
|
||||||
|
hasDelete={true}
|
||||||
|
on:remove={removeReference}
|
||||||
|
on:restore={restoreReference}
|
||||||
|
on:fulldelete={fullDelete}
|
||||||
|
></PreviewFile>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</Sortable>
|
</Sortable>
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
<script>
|
<script>
|
||||||
import {getContext} from "svelte";
|
import {getContext} from "svelte";
|
||||||
import {insertEdges} from "./reference";
|
import {
|
||||||
|
fullDeleteRecord,
|
||||||
|
graphToReferences,
|
||||||
|
insertEdges,
|
||||||
|
removeReferenceFromGraph,
|
||||||
|
restoreReferenceToGraph
|
||||||
|
} 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;
|
||||||
@@ -16,11 +21,7 @@
|
|||||||
$: errorMessage = getErrorMessage(validationErrors, field.name);
|
$: errorMessage = getErrorMessage(validationErrors, field.name);
|
||||||
|
|
||||||
|
|
||||||
$: references = graph.edges
|
$: references = graphToReferences(graph,record,field)
|
||||||
.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 collections = channel.schemas.filter((aschema) =>
|
let collections = channel.schemas.filter((aschema) =>
|
||||||
field.collections.includes(aschema.name)
|
field.collections.includes(aschema.name)
|
||||||
@@ -28,27 +29,25 @@
|
|||||||
|
|
||||||
function removeReference(e) {
|
function removeReference(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
graph.edges = graph.edges.filter(
|
graph.edges = removeReferenceFromGraph(graph,field,e.detail)
|
||||||
(edge) => !(edge.target === e.detail && edge.field === field.name)
|
}
|
||||||
);
|
|
||||||
|
function restoreReference(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
graph.edges = restoreReferenceToGraph(graph,field,e.detail)
|
||||||
|
}
|
||||||
|
|
||||||
|
function fullDelete(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
graph.edges = fullDeleteRecord(channel,graph, field, e.detail)
|
||||||
}
|
}
|
||||||
|
|
||||||
function reorder(e) {
|
function reorder(e) {
|
||||||
|
|
||||||
graph.edges = sortByField(e.detail.source, e.detail.target, graph.edges, field.name, references);
|
graph.edges = sortByField(e.detail.source, e.detail.target, graph.edges, field.name, references);
|
||||||
}
|
}
|
||||||
|
|
||||||
function insert(e) {
|
function insert(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
// axios.post(channel.lucentUrl + "/edges/insert-many", {
|
|
||||||
// source: record.id,
|
|
||||||
// sourceSchema: record.schema,
|
|
||||||
// targetSchema: e.detail.schema,
|
|
||||||
// field: field.name,
|
|
||||||
// targets: e.detail.records.map(r => r.id),
|
|
||||||
// }).then(function (response) {
|
|
||||||
// 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,13 +68,16 @@
|
|||||||
</div>
|
</div>
|
||||||
{#if references.length > 0}
|
{#if references.length > 0}
|
||||||
<Sortable sortableClass="row row-cols-3 mt-3" on:update={reorder}>
|
<Sortable sortableClass="row row-cols-3 mt-3" on:update={reorder}>
|
||||||
{#each references as reference (reference.id)}
|
{#each references as reference (reference.record.id)}
|
||||||
<div>
|
<div>
|
||||||
<PreviewReference
|
<PreviewReference
|
||||||
{graph}
|
{graph}
|
||||||
record={reference}
|
record={reference.record}
|
||||||
|
edge={reference.edge}
|
||||||
hasDelete={true}
|
hasDelete={true}
|
||||||
on:remove={removeReference}
|
on:remove={removeReference}
|
||||||
|
on:restore={restoreReference}
|
||||||
|
on:fulldelete={fullDelete}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import {uniqBy} from "lodash";
|
import {uniqBy} from "lodash";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
export function insertEdges(graph, sourceRecord, targetRecords, fieldName, action = "") {
|
export function insertEdges(graph, sourceRecord, targetRecords, fieldName, action = "") {
|
||||||
let newEdges = targetRecords.map((r) => {
|
let newEdges = targetRecords.map((r) => {
|
||||||
@@ -22,3 +23,49 @@ export function insertEdges(graph, sourceRecord, targetRecords, fieldName, actio
|
|||||||
graph.edges = uniqBy([...replacedEdges, ...newEdges], (edge) => edge.source + edge.target + edge.field + edge.depth);
|
graph.edges = uniqBy([...replacedEdges, ...newEdges], (edge) => edge.source + edge.target + edge.field + edge.depth);
|
||||||
return graph;
|
return graph;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function graphToReferences(graph,record,field){
|
||||||
|
return graph.edges
|
||||||
|
.filter((edge) => edge.field === field.name)
|
||||||
|
.map((edge) => {
|
||||||
|
return {
|
||||||
|
record: graph.records.find((increc) => increc.id === edge.target && record.id === edge.source),
|
||||||
|
edge: edge
|
||||||
|
};
|
||||||
|
}).filter((rec) => (rec.record?.id ? true : false)) ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeReferenceFromGraph(graph,field,id){
|
||||||
|
return graph.edges.map(
|
||||||
|
(edge) => {
|
||||||
|
if(edge.target === id && edge.field === field.name){
|
||||||
|
edge._isTrashed = true;
|
||||||
|
}
|
||||||
|
return edge;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function restoreReferenceToGraph(graph,field,id){
|
||||||
|
return graph.edges.map(
|
||||||
|
(edge) => {
|
||||||
|
if(edge.target === id && edge.field === field.name){
|
||||||
|
edge._isTrashed = false;
|
||||||
|
}
|
||||||
|
return edge;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function fullDeleteRecord(channel,graph,field,id){
|
||||||
|
axios
|
||||||
|
.post(channel.lucentUrl + "/records/status/trashed" , {
|
||||||
|
records: [id]
|
||||||
|
});
|
||||||
|
|
||||||
|
return graph.edges.filter(
|
||||||
|
(edge) => !(edge.target === id && edge.field === field.name)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
const channel = getContext("channel");
|
const channel = getContext("channel");
|
||||||
export let record;
|
export let record;
|
||||||
|
export let edge;
|
||||||
export let hasDelete = false;
|
export let hasDelete = false;
|
||||||
export let hasInsert = false;
|
export let hasInsert = false;
|
||||||
|
|
||||||
@@ -23,6 +24,16 @@
|
|||||||
dispatch("remove", record.id);
|
dispatch("remove", record.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function restore(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
dispatch("restore", record.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fullDelete(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
dispatch("fulldelete", record.id);
|
||||||
|
}
|
||||||
|
|
||||||
function insert(e, preset) {
|
function insert(e, preset) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
let html = htmlurl(channel, record, preset)
|
let html = htmlurl(channel, record, preset)
|
||||||
@@ -37,7 +48,7 @@
|
|||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="preview-file">
|
<div class="preview-file" class:is-trashed={edge._isTrashed}>
|
||||||
<div style="display: flex;align-items: center;gap: 10px;">
|
<div style="display: flex;align-items: center;gap: 10px;">
|
||||||
<div class="image">
|
<div class="image">
|
||||||
<Preview {record} size="small"/>
|
<Preview {record} size="small"/>
|
||||||
@@ -49,6 +60,9 @@
|
|||||||
href="{channel.lucentUrl}/records/{record.id}"
|
href="{channel.lucentUrl}/records/{record.id}"
|
||||||
>
|
>
|
||||||
{cardTitle}
|
{cardTitle}
|
||||||
|
{#if edge._isTrashed}
|
||||||
|
<span class="trashed-text">Trashed</span>
|
||||||
|
{/if}
|
||||||
</a>
|
</a>
|
||||||
<small class="d-block">
|
<small class="d-block">
|
||||||
from {schema.label}
|
from {schema.label}
|
||||||
@@ -78,12 +92,30 @@
|
|||||||
{/if}
|
{/if}
|
||||||
{#if hasDelete}
|
{#if hasDelete}
|
||||||
<div class="reference-action">
|
<div class="reference-action">
|
||||||
<button
|
{#if edge._isTrashed}
|
||||||
class="button"
|
<button
|
||||||
on:click={remove}
|
title="Restore"
|
||||||
>
|
class="button"
|
||||||
<Icon icon="trash-can"/>
|
on:click={restore}
|
||||||
</button>
|
>
|
||||||
|
<Icon icon="undo"/>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
title="Delete from everywhere"
|
||||||
|
class="button"
|
||||||
|
on:click={fullDelete}
|
||||||
|
>
|
||||||
|
<Icon icon="destroy"/>
|
||||||
|
</button>
|
||||||
|
{:else}
|
||||||
|
<button
|
||||||
|
title="Remove"
|
||||||
|
class="button"
|
||||||
|
on:click={remove}
|
||||||
|
>
|
||||||
|
<Icon icon="trash-can"/>
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,21 +10,32 @@
|
|||||||
const channel = getContext("channel");
|
const channel = getContext("channel");
|
||||||
export let graph;
|
export let graph;
|
||||||
export let record;
|
export let record;
|
||||||
|
export let edge;
|
||||||
export let hasDelete = false;
|
export let hasDelete = false;
|
||||||
|
|
||||||
let schema = channel.schemas.find((aschema) => aschema.name === record.schema);
|
let schema = channel.schemas.find((aschema) => aschema.name === record.schema);
|
||||||
let cardTitle = previewTitle(channel.schemas, record, graph);
|
let cardTitle = previewTitle(channel.schemas, record, graph);
|
||||||
const cardImageEdge = graph.edges.find(e => e.source === record.id && e.field === schema.cardImage);
|
const cardImageEdge = graph.edges.find(e => e.source === record.id && e.field === schema.cardImage);
|
||||||
let cardImageRecord = graph.records.find(r => r.id === cardImageEdge?.target);
|
let cardImageRecord = graph.records.find(r => r.id === cardImageEdge?.target);
|
||||||
|
|
||||||
function remove(e) {
|
function remove(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
dispatch("remove", record.id);
|
dispatch("remove", record.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function restore(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
dispatch("restore", record.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fullDelete(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
dispatch("fulldelete", record.id);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<div class="preview-reference">
|
<div class="preview-reference" class:is-trashed={edge._isTrashed}>
|
||||||
<div style="display: flex;align-items: center;gap: 10px;">
|
<div style="display: flex;align-items: center;gap: 10px;">
|
||||||
|
|
||||||
{#if cardImageRecord}
|
{#if cardImageRecord}
|
||||||
@@ -38,7 +49,12 @@
|
|||||||
class="record-title"
|
class="record-title"
|
||||||
href="{channel.lucentUrl}/records/{record.id}"
|
href="{channel.lucentUrl}/records/{record.id}"
|
||||||
>
|
>
|
||||||
|
|
||||||
{cardTitle}
|
{cardTitle}
|
||||||
|
{#if edge._isTrashed}
|
||||||
|
<span class="trashed-text">Trashed</span>
|
||||||
|
{/if}
|
||||||
|
|
||||||
</a>
|
</a>
|
||||||
<small class="d-block">
|
<small class="d-block">
|
||||||
from {schema.label}
|
from {schema.label}
|
||||||
@@ -53,12 +69,30 @@
|
|||||||
</div>
|
</div>
|
||||||
{#if hasDelete}
|
{#if hasDelete}
|
||||||
<div class="reference-action">
|
<div class="reference-action">
|
||||||
<button
|
{#if edge._isTrashed}
|
||||||
class="button"
|
<button
|
||||||
on:click={remove}
|
title="Restore"
|
||||||
>
|
class="button"
|
||||||
<Icon icon="trash-can"/>
|
on:click={restore}
|
||||||
</button>
|
>
|
||||||
|
<Icon icon="undo"/>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
title="Delete from everywhere"
|
||||||
|
class="button"
|
||||||
|
on:click={fullDelete}
|
||||||
|
>
|
||||||
|
<Icon icon="destroy"/>
|
||||||
|
</button>
|
||||||
|
{:else}
|
||||||
|
<button
|
||||||
|
title="Remove"
|
||||||
|
class="button"
|
||||||
|
on:click={remove}
|
||||||
|
>
|
||||||
|
<Icon icon="trash-can"/>
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,6 +5,17 @@
|
|||||||
gap: 10px;
|
gap: 10px;
|
||||||
background: var(--p10);
|
background: var(--p10);
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
|
|
||||||
|
&.is-trashed{
|
||||||
|
border: 2px solid var(--err10);
|
||||||
|
background: var(--p20);
|
||||||
|
}
|
||||||
|
.trashed-text{
|
||||||
|
background: var(--err10);
|
||||||
|
font-size: 12px;
|
||||||
|
padding:2px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.image{
|
.image{
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use Lucent\Record\Manager;
|
|||||||
use Lucent\Record\QueryRecord;
|
use Lucent\Record\QueryRecord;
|
||||||
use Lucent\Record\RecordService;
|
use Lucent\Record\RecordService;
|
||||||
use Lucent\Record\Status;
|
use Lucent\Record\Status;
|
||||||
|
use Lucent\Schema\SingletonSchema;
|
||||||
use Lucent\Schema\System;
|
use Lucent\Schema\System;
|
||||||
use Lucent\Schema\Validator\ValidatorException;
|
use Lucent\Schema\Validator\ValidatorException;
|
||||||
use Lucent\Svelte\Svelte;
|
use Lucent\Svelte\Svelte;
|
||||||
@@ -50,8 +51,9 @@ class RecordController extends Controller
|
|||||||
|
|
||||||
$users = $this->accountService->all();
|
$users = $this->accountService->all();
|
||||||
$schema = $this->channelService->getSchema($schemaName)->get();
|
$schema = $this->channelService->getSchema($schemaName)->get();
|
||||||
|
|
||||||
$urlParams = $request->all();
|
$urlParams = $request->all();
|
||||||
$sort = data_get($urlParams, "sort") ?? $schema->sortBy;
|
$sort = data_get($urlParams, "sort") ?? $schema->sortBy ?? "";
|
||||||
$filter = data_get($urlParams, "filter") ?? [];
|
$filter = data_get($urlParams, "filter") ?? [];
|
||||||
|
|
||||||
$arguments = array_merge([
|
$arguments = array_merge([
|
||||||
@@ -80,6 +82,13 @@ class RecordController extends Controller
|
|||||||
|
|
||||||
|
|
||||||
$records = $graph->getRootRecords()->toArray();
|
$records = $graph->getRootRecords()->toArray();
|
||||||
|
if(get_class($schema) === SingletonSchema::class){
|
||||||
|
$id = $records[0]->id ?? null;
|
||||||
|
if(empty($id)){
|
||||||
|
return redirect($this->channelService->channel->lucentUrl."/records/new?schema=".$schemaName);
|
||||||
|
}
|
||||||
|
return redirect($this->channelService->channel->lucentUrl."/records/".$id);
|
||||||
|
}
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
"schemas" => $this->channelService->channel->schemas,
|
"schemas" => $this->channelService->channel->schemas,
|
||||||
@@ -341,20 +350,15 @@ class RecordController extends Controller
|
|||||||
|
|
||||||
public function status(Request $request)
|
public function status(Request $request)
|
||||||
{
|
{
|
||||||
|
|
||||||
$ids = array_map(fn($rec) => $rec["id"], $request->input("records"));
|
|
||||||
|
|
||||||
|
|
||||||
$this->recordService->changeStatusBulk(
|
$this->recordService->changeStatusBulk(
|
||||||
status: $request->route("status"),
|
status: $request->route("status"),
|
||||||
recordsIds: $ids,
|
recordsIds: $request->input("records"),
|
||||||
);
|
);
|
||||||
return ok();
|
return ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function emptyTrash(Request $request)
|
public function emptyTrash(Request $request)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->recordService->emptyTrash($request->route("schemaName"));
|
$this->recordService->emptyTrash($request->route("schemaName"));
|
||||||
return redirect($this->channelService->channel->lucentUrl . "/content/" . $request->route("schemaName"));
|
return redirect($this->channelService->channel->lucentUrl . "/content/" . $request->route("schemaName"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ class CollectionSchema implements Schema
|
|||||||
function __construct(
|
function __construct(
|
||||||
public string $name,
|
public string $name,
|
||||||
public string $label,
|
public string $label,
|
||||||
|
|
||||||
public array $visible,
|
public array $visible,
|
||||||
public array $groups,
|
public array $groups,
|
||||||
public Collection $fields,
|
public Collection $fields,
|
||||||
|
|||||||
@@ -32,6 +32,19 @@ class SchemaService
|
|||||||
read: $schemaArr["read"] ?? [],
|
read: $schemaArr["read"] ?? [],
|
||||||
write: $schemaArr["write"] ?? [],
|
write: $schemaArr["write"] ?? [],
|
||||||
),
|
),
|
||||||
|
"singleton" => new SingletonSchema(
|
||||||
|
name: $schemaArr["name"],
|
||||||
|
label: $schemaArr["label"],
|
||||||
|
groups: $schemaArr["groups"] ?? [],
|
||||||
|
fields: (new Collection($schemaArr["fields"]))->map([$this, 'mapFields']),
|
||||||
|
folder: $schemaArr["folder"] ?? "",
|
||||||
|
color: $schemaArr["color"] ?? "",
|
||||||
|
cardTitle: $schemaArr["titleTemplate"] ?? $schemaArr["cardTitle"] ?? null,
|
||||||
|
cardImage: $schemaArr["cardImage"] ?? null,
|
||||||
|
revisions: $schemaArr["revisions"] ?? 0,
|
||||||
|
read: $schemaArr["read"] ?? [],
|
||||||
|
write: $schemaArr["write"] ?? [],
|
||||||
|
),
|
||||||
"files" => new FilesSchema(
|
"files" => new FilesSchema(
|
||||||
name: $schemaArr["name"],
|
name: $schemaArr["name"],
|
||||||
label: $schemaArr["label"],
|
label: $schemaArr["label"],
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lucent\Schema;
|
||||||
|
|
||||||
|
use Lucent\Primitive\Collection;
|
||||||
|
|
||||||
|
class SingletonSchema implements Schema
|
||||||
|
{
|
||||||
|
public Type $type = Type::COLLECTION;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Collection<FieldInterface> $fields
|
||||||
|
*/
|
||||||
|
function __construct(
|
||||||
|
public string $name,
|
||||||
|
public string $label,
|
||||||
|
public array $groups,
|
||||||
|
public Collection $fields,
|
||||||
|
public string $folder = "",
|
||||||
|
public string $color = "",
|
||||||
|
public ?string $cardTitle = null,
|
||||||
|
public ?string $cardImage = null,
|
||||||
|
public int $revisions = 0,
|
||||||
|
public array $read = [],
|
||||||
|
public array $write = [],
|
||||||
|
)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ use Lucent\Record\QueryRecord;
|
|||||||
use Lucent\Schema\CollectionSchema;
|
use Lucent\Schema\CollectionSchema;
|
||||||
use Lucent\Schema\FieldInterface;
|
use Lucent\Schema\FieldInterface;
|
||||||
use Lucent\Schema\FilesSchema;
|
use Lucent\Schema\FilesSchema;
|
||||||
|
use Lucent\Schema\SingletonSchema;
|
||||||
use Mustache_Engine;
|
use Mustache_Engine;
|
||||||
|
|
||||||
class ViewModel
|
class ViewModel
|
||||||
@@ -23,7 +24,7 @@ class ViewModel
|
|||||||
$schema = $this->channelService->getSchema($record->schema)->get();
|
$schema = $this->channelService->getSchema($record->schema)->get();
|
||||||
if (empty($schema->titleTemplate)) {
|
if (empty($schema->titleTemplate)) {
|
||||||
$title = match (get_class($schema)) {
|
$title = match (get_class($schema)) {
|
||||||
CollectionSchema::class => $record->data[$schema->fields->filter(fn(FieldInterface $f) => $f->info->name === "text")->first()->name],
|
CollectionSchema::class,SingletonSchema::class => $record->data[$schema->fields->filter(fn(FieldInterface $f) => $f->info->name === "text")->first()->name],
|
||||||
FilesSchema::class => $record->_file->path,
|
FilesSchema::class => $record->_file->path,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user