Compare commits

..

2 Commits

Author SHA1 Message Date
lexx a54200c5e5 build 2024-05-22 17:05:56 +03:00
lexx 069ae72705 fix sort 2024-05-22 17:05:25 +03:00
232 changed files with 2844 additions and 11459 deletions
+3 -6
View File
@@ -11,16 +11,13 @@
"php": "^8.2", "php": "^8.2",
"guzzlehttp/guzzle": "^7.2", "guzzlehttp/guzzle": "^7.2",
"intervention/image": "^2.7", "intervention/image": "^2.7",
"phpoption/phpoption": "^1.9",
"spatie/image-optimizer": "^1.6", "spatie/image-optimizer": "^1.6",
"staudenmeir/laravel-cte": "^1.0", "staudenmeir/laravel-cte": "^1.0",
"ext-pdo": "*", "ext-pdo": "*"
"opis/json-schema": "^2.3",
"symfony/yaml": "^7.0",
"spatie/laravel-data": "^4.4"
}, },
"require-dev": { "require-dev": {
"phpstan/phpstan": "^1.8", "phpstan/phpstan": "^1.8"
"laravel/framework": "^10.10"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
Generated
+459 -5785
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"main.js": { "main.js": {
"file": "assets/main.7c3e8b7b.js", "file": "assets/main.c1fd60c7.js",
"src": "main.js", "src": "main.js",
"isEntry": true, "isEntry": true,
"css": [ "css": [
+2 -2
View File
@@ -23,7 +23,7 @@
export let data; export let data;
// export let layout; // export let layout;
export let channel; export let channel;
export let sidebar;
export let axios; export let axios;
export let readableSchemas; export let readableSchemas;
@@ -35,7 +35,7 @@
</script> </script>
<Navbar {sidebar}/> <Navbar schema={data.schema}/>
<svelte:component this={components[view]} {title} {...data}/> <svelte:component this={components[view]} {title} {...data}/>
+89 -10
View File
@@ -1,11 +1,26 @@
<script> <script>
import Avatar from "./account/Avatar.svelte"; import Avatar from "./account/Avatar.svelte";
import NavbarMenu from "./NavbarMenu.svelte";
import {getContext} from "svelte"; import {getContext} from "svelte";
export let sidebar; export let schema;
const channel = getContext("channel"); const channel = getContext("channel");
const readableSchemas = getContext("readableSchemas");
const user = getContext("user"); const user = getContext("user");
let contentIsOpen = false; let contentIsOpen = false;
const fileSchemas = readableSchemas.filter((sc) => sc.type === "files");
const otherSchemas = readableSchemas.filter((sc) => !sc.isEntry && sc.type === "collection");
let filesIsActive = false;
let otherIsActive = false;
if(schema){
filesIsActive = fileSchemas.filter(s => s.name === schema.name).length > 0;
otherIsActive = otherSchemas.filter(s => s.name === schema.name).length > 0;
}
</script> </script>
<nav class="lx-nav"> <nav class="lx-nav">
@@ -14,7 +29,12 @@
<button on:click={(e) => contentIsOpen = true} class="btn btn-primary btn-sm d-xxl-none">« Content</button> <button on:click={(e) => contentIsOpen = true} class="btn btn-primary btn-sm d-xxl-none">« Content</button>
</div> </div>
<div class="d-flex align-items-center "> <div class="d-flex align-items-center ">
<a class="nav-item" href="{channel.lucentUrl}">{channel.name}</a>
<a class="nav-item" href="{channel.lucentUrl}/members">Members</a>
{#if channel.generateCommand}
<a href="{channel.lucentUrl}/build-report" class="btn btn-outline-primary btn-sm d-">Build website</a>
{/if}
<!-- <div>--> <!-- <div>-->
<!-- <form method="GET">--> <!-- <form method="GET">-->
<!-- <input type="search" name="filter[search_regex]" placeholder="Search"--> <!-- <input type="search" name="filter[search_regex]" placeholder="Search"-->
@@ -22,12 +42,7 @@
<!-- </form>--> <!-- </form>-->
<!-- </div>--> <!-- </div>-->
</div> </div>
<div class="d-flex align-items-center "> <div>
<a class="nav-item" href="{channel.lucentUrl}/members">Members</a>
{#if channel.generateCommand}
<a href="{channel.lucentUrl}/build-report" class="btn btn-outline-primary btn-sm d-">Build website</a>
{/if}
<a class="nav-item" href="{channel.lucentUrl}/profile"> <a class="nav-item" href="{channel.lucentUrl}/profile">
<Avatar side="28" name={user.name}/> <Avatar side="28" name={user.name}/>
</a> </a>
@@ -36,12 +51,76 @@
</nav> </nav>
<div class="offcanvas offcanvas-start d-xxl-block show border-0 bg-primary-subtle " class:d-none={!contentIsOpen} <div class="offcanvas offcanvas-start d-xxl-block show border-0 bg-light-subtle" class:d-none={!contentIsOpen}
data-bs-scroll="true" style="padding-top:36px " data-bs-scroll="true"
data-bs-backdrop="false" data-bs-backdrop="false"
tabindex="-1" aria-labelledby="offcanvasScrollingLabel"> tabindex="-1" aria-labelledby="offcanvasScrollingLabel">
<!-- <div class="offcanvas-header">-->
<!-- <h5 class="offcanvas-title" id="offcanvasScrollingLabel">Content</h5>-->
<!-- </div>-->
<div class="offcanvas-body"> <div class="offcanvas-body">
<button on:click={(e) => contentIsOpen = false} class="btn btn-primary btn-sm d-xxl-none mb-4">« close</button> <button on:click={(e) => contentIsOpen = false} class="btn btn-primary btn-sm d-xxl-none mb-4">« close</button>
{@html sidebar} <div class="accordion">
<div class="accordion-item">
<h2 class="accordion-header" id="panelsStayOpen-headingMain">
<button class="accordion-button" type="button" data-bs-toggle="collapse"
data-bs-target="#panelsStayOpen-collapseMain" aria-expanded="true"
aria-controls="panelsStayOpen-collapseMain">
Main
</button>
</h2>
<div id="panelsStayOpen-collapseMain" class="accordion-collapse collapse show"
aria-labelledby="panelsStayOpen-headingMain">
<div class="accordion-body">
<NavbarMenu
schemas={ readableSchemas.filter((sc) => sc.isEntry)}
schema={schema}
/>
</div>
</div>
</div>
{#if otherSchemas.length > 0}
<div class="accordion-item">
<h2 class="accordion-header" id="panelsStayOpen-headingOther">
<button class="accordion-button" class:collapsed={!otherIsActive} type="button" data-bs-toggle="collapse"
data-bs-target="#panelsStayOpen-collapseOther" aria-expanded={otherIsActive}
aria-controls="panelsStayOpen-collapseOther">
Other
</button>
</h2>
<div id="panelsStayOpen-collapseOther" class="accordion-collapse collapse"
class:show={otherIsActive}
aria-labelledby="panelsStayOpen-headingOther">
<div class="accordion-body">
<NavbarMenu
schemas={ otherSchemas}
schema={schema}
/>
</div>
</div>
</div>
{/if}
{#if fileSchemas.length > 0}
<div class="accordion-item">
<h2 class="accordion-header" id="panelsStayOpen-headingFS">
<button class="accordion-button " class:collapsed={!filesIsActive} type="button" data-bs-toggle="collapse"
data-bs-target="#panelsStayOpen-collapseFS" aria-expanded={filesIsActive}
aria-controls="panelsStayOpen-collapseFS">
Filesystem
</button>
</h2>
<div id="panelsStayOpen-collapseFS" class="accordion-collapse collapse" class:show={filesIsActive}
aria-labelledby="panelsStayOpen-headingFS">
<div class="accordion-body">
<NavbarMenu
schemas={ fileSchemas}
schema={schema}
/>
</div>
</div>
</div>
{/if}
</div>
</div> </div>
</div> </div>
+16
View File
@@ -0,0 +1,16 @@
<script>
import {getContext} from "svelte";
const channel = getContext("channel");
export let schemas;
export let schema;
</script>
<div class="list-group list-group-flush">
{#each schemas as aschema}
<a class="list-group-item list-group-item-action" class:active={aschema.name === schema?.name}
aria-current="page"
href="{channel.lucentUrl}/content/{aschema.name}">{aschema.label}</a>
{/each}
</div>
-33
View File
@@ -1,33 +0,0 @@
<script>
import {onDestroy, onMount} from "svelte";
import offcanvas from "bootstrap/js/src/offcanvas.js";
export let title = "";
let offCanvasEl;
let offCanvasInstance;
export function show() {
if(!offCanvasInstance){
offCanvasInstance = new offcanvas(offCanvasEl);
}
offCanvasInstance.show();
}
onMount(()=>{
offCanvasInstance = new offcanvas(offCanvasEl);
});
export function hide() {
offCanvasInstance.hide();
}
</script>
<div bind:this={offCanvasEl} class="offcanvas offcanvas-end" tabindex="-1"
aria-labelledby="offcanvasEditContent">
<div class="offcanvas-header">
<h5 class="offcanvas-title">{title}</h5>
<button type="button" on:click={hide} class="btn-close" data-bs-dismiss="offcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body" style="overflow: auto">
<slot/>
</div>
</div>
@@ -1,13 +0,0 @@
<script>
import {uniqueId} from "lodash";
export let label = "";
let id = uniqueId();
</script>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id={id} checked>
<label class="form-check-label" for={id}>{label}</label>
</div>
+12 -11
View File
@@ -1,10 +1,11 @@
<script> <script>
import {getContext} from "svelte"; import {getContext} from "svelte";
import Preview from "../files/Preview.svelte";
import {selectRecord} from "./functions/recordSelect.js"; import {selectRecord} from "./functions/recordSelect.js";
import Preview from "../newPreview/Preview.svelte";
const channel = getContext("channel"); const channel = getContext("channel");
export let schema;
export let records; export let records;
export let isWritable; export let isWritable;
export let selected = []; export let selected = [];
@@ -16,42 +17,42 @@
</script> </script>
<div class="row" style="max-width:1000px"> <div class="row" style="max-width:1000px">
{#each records as queryRecord (queryRecord.record.id)} {#each records as record (record.id)}
<div class="col-6 col-md-4"> <div class="col-6 col-md-4">
<div <div
class="file-wrapper rounded p-2 mb-4 bg-light" class="file-wrapper rounded p-2 mb-4 bg-light"
class:selected={selected.includes(queryRecord)} class:selected={selected.includes(record)}
> >
{#if isWritable} {#if isWritable}
<div class="form-check"> <div class="form-check">
<input <input
on:change={() => select(queryRecord.record)} on:change={() => select(record)}
class="form-check-input " class="form-check-input "
type="checkbox" type="checkbox"
checked={selected.find( checked={selected.find(
(r) => r.id === queryRecord.record.id (r) => r.id === record.id
)} )}
value={queryRecord} value={record}
/> />
</div> </div>
{/if} {/if}
<div class="d-flex justify-content-center"> <div class="d-flex justify-content-center">
<Preview record={queryRecord.record} /> <Preview {record} size="medium"/>
</div> </div>
<a <a
href="{channel.lucentUrl}/records/{queryRecord.record.id}" href="{channel.lucentUrl}/records/{record.id}"
title={queryRecord.record._file.path} title={record._file.path}
class="d-block text-center overflow-hidden text-nowrap my-2 " class="d-block text-center overflow-hidden text-nowrap my-2 "
style=" style="
text-overflow: ellipsis; text-overflow: ellipsis;
font-size: 13px; font-size: 13px;
color: #333; color: #333;
">{queryRecord.record._file.path}</a ">{record._file.path}</a
> >
<span <span
class="lx-small-text text-muted d-block text-center" class="lx-small-text text-muted d-block text-center"
>{queryRecord.record._file.mime}</span >{record._file.mime}</span
> >
</div> </div>
</div> </div>
+5 -3
View File
@@ -1,16 +1,16 @@
<script> <script>
// import Tools from "./tools/Tools.svelte"; import Tools from "./tools/Tools.svelte";
import Pagination from "./pagination/Pagination.svelte"; import Pagination from "./pagination/Pagination.svelte";
import ActionsOnSelected from "./ActionsOnSelected.svelte"; import ActionsOnSelected from "./ActionsOnSelected.svelte";
import Table from "./Table.svelte"; import Table from "./Table.svelte";
import {getContext} from "svelte"; import {getContext} from "svelte";
import Grid from "./Grid.svelte"; import Grid from "./Grid.svelte";
import Tools from "./tools/Tools.svelte";
const axios = getContext("axios"); const axios = getContext("axios");
export let schema; export let schema;
export let users; export let users;
export let records; export let records;
export let graph;
// export let visibleFields; // export let visibleFields;
export let systemFields; export let systemFields;
export let sortParam; export let sortParam;
@@ -47,7 +47,7 @@
</script> </script>
<div class="wrapper-large transparent "> <div class="wrapper-large transparent ">
<div class="lx-card mb-4 mt-0"> <div class="lx-card mb-4 {inModal ? 'mt-0' : 'mt-5'}">
<h3 class="header-normal mb-5 "> <h3 class="header-normal mb-5 ">
{schema.label} {schema.label}
</h3> </h3>
@@ -62,6 +62,7 @@
{sortField} {sortField}
{operators} {operators}
{filter} {filter}
{graph}
{inModal} {inModal}
{modalUrl} {modalUrl}
{isWritable} {isWritable}
@@ -72,6 +73,7 @@
{#if schema.type === "collection"} {#if schema.type === "collection"}
<Table <Table
{records} {records}
{graph}
{schema} {schema}
{sortParam} {sortParam}
{sortField} {sortField}
+8 -7
View File
@@ -7,7 +7,8 @@
export let schema; export let schema;
export let users; export let users;
export let queryRecord; export let graph;
export let record;
export let sortParam; export let sortParam;
export let sortField; export let sortField;
export let visibleColumns; export let visibleColumns;
@@ -19,7 +20,7 @@
class="field-ui-{field.info.name}" class="field-ui-{field.info.name}"
class:is-sort={field.name === sortField.name} class:is-sort={field.name === sortField.name}
> >
<RenderField {queryRecord} {field}/> <RenderField {record} {schema} {graph} {field}/>
</td> </td>
{/each} {/each}
{#if schema.visible.includes("status")} {#if schema.visible.includes("status")}
@@ -27,7 +28,7 @@
class="text-center" class="text-center"
class:is-sort={"-status" == sortParam || "status" == sortParam} class:is-sort={"-status" == sortParam || "status" == sortParam}
> >
<Status status={queryRecord.record.status}/> <Status status={record.status}/>
</td> </td>
{/if} {/if}
{#if schema.visible.includes("_sys.createdBy")} {#if schema.visible.includes("_sys.createdBy")}
@@ -35,7 +36,7 @@
class="text-center" class="text-center"
class:is-sort={"-_sys.createdBy" == sortParam || "_sys.createdBy" == sortParam} class:is-sort={"-_sys.createdBy" == sortParam || "_sys.createdBy" == sortParam}
> >
<Avatar name={usernameById(users, queryRecord.record._sys.createdBy)} side={24}/> <Avatar name={usernameById(users, record._sys.createdBy)} side={24}/>
</td> </td>
{/if} {/if}
{#if schema.visible.includes("_sys.updatedBy")} {#if schema.visible.includes("_sys.updatedBy")}
@@ -43,16 +44,16 @@
class="text-center" class="text-center"
class:is-sort={"-_sys.updatedBy" == sortParam || "_sys.updatedBy" == sortParam} class:is-sort={"-_sys.updatedBy" == sortParam || "_sys.updatedBy" == sortParam}
> >
<Avatar name={usernameById(users, queryRecord.record._sys.updatedBy)} side={24}/> <Avatar name={usernameById(users, record._sys.updatedBy)} side={24}/>
</td> </td>
{/if} {/if}
{#if schema.visible.includes("_sys.createdAt")} {#if schema.visible.includes("_sys.createdAt")}
<td class:is-sort={"-_sys.createdAt" == sortParam || "_sys.createdAt" == sortParam}> <td class:is-sort={"-_sys.createdAt" == sortParam || "_sys.createdAt" == sortParam}>
{friendlyDate(queryRecord.record._sys.createdAt)} {friendlyDate(record._sys.createdAt)}
</td> </td>
{/if} {/if}
{#if schema.visible.includes("_sys.updatedAt")} {#if schema.visible.includes("_sys.updatedAt")}
<td class:is-sort={"-_sys.updatedAt" == sortParam || "_sys.updatedAt" == sortParam}> <td class:is-sort={"-_sys.updatedAt" == sortParam || "_sys.updatedAt" == sortParam}>
{friendlyDate(queryRecord.record._sys.updatedAt)} {friendlyDate(record._sys.updatedAt)}
</td> </td>
{/if} {/if}
+7 -3
View File
@@ -29,13 +29,17 @@
file: File, file: File,
}; };
export let field; export let field;
export let queryRecord; export let schema;
export let record;
export let graph;
</script> </script>
<svelte:component <svelte:component
this={renderElements[field.info.name]} this={renderElements[field.info.name]}
value={queryRecord.record.data[field.name]} value={record.data[field.name]}
{queryRecord} {record}
{graph}
{schema}
{field} {field}
/> />
+11 -9
View File
@@ -11,6 +11,7 @@
export let schema; export let schema;
export let users; export let users;
export let records; export let records;
export let graph;
export let systemFields; export let systemFields;
export let sortParam; export let sortParam;
export let sortField; export let sortField;
@@ -63,7 +64,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{#each records as queryRecord (queryRecord.record.id)} {#each records as record (record.id)}
<tr> <tr>
<td class="title-td"> <td class="title-td">
<div <div
@@ -73,13 +74,13 @@
{#if isWritable} {#if isWritable}
<div class="form-check"> <div class="form-check">
<input <input
on:change={() => select(queryRecord.record)} on:change={() => select(record)}
class="form-check-input " class="form-check-input "
type="checkbox" type="checkbox"
checked={selected.find( checked={selected.find(
(r) => r.id === queryRecord.record.id (r) => r.id === record.id
)} )}
value={queryRecord} value={record}
/> />
</div> </div>
{/if} {/if}
@@ -87,20 +88,20 @@
<a <a
class="me-2 text-decoration-none text-dark fs-6" class="me-2 text-decoration-none text-dark fs-6"
href="{channel.lucentUrl}/records/{queryRecord.record.id}" href="{channel.lucentUrl}/records/{record.id}"
target={inModal ? "_blank" : "_self"} target={inModal ? "_blank" : "_self"}
title={previewTitle(queryRecord.record)} title={previewTitle(channel.schemas, record, graph)}
data-bs-toggle="tooltip" data-bs-placement="left" data-bs-toggle="tooltip" data-bs-placement="left"
> >
{previewTitle(queryRecord.record)} {previewTitle(channel.schemas, record, graph)}
</a> </a>
</div> </div>
<div> <div>
<Avatar <Avatar
name={usernameById( name={usernameById(
users, users,
queryRecord.record._sys.updatedBy record._sys.updatedBy
)} )}
side={24} side={24}
/> />
@@ -108,7 +109,8 @@
</div> </div>
</td> </td>
<RecordRow <RecordRow
{queryRecord} {record}
{graph}
{schema} {schema}
{visibleColumns} {visibleColumns}
{sortParam} {sortParam}
+9 -2
View File
@@ -1,11 +1,18 @@
<script> <script>
import Preview from "../../files/Preview.svelte"; import Preview from "../../files/Preview.svelte";
export let queryRecord; export let record;
export let field; export let field;
export let graph;
let filePreviews = queryRecord?._children[field.name];
let filePreviews = graph.edges?.filter((ed) => ed.field === field.name && ed.source === record.id)
.map((ed) => graph.records.find((r) => r.id === ed.target));
// if (edges[0]) {
// firstRecord = record._children.find((r) => r.data.id === edges[0].to);
// }
console.log(filePreviews)
</script> </script>
<!-- {#if firstRecord} <!-- {#if firstRecord}
@@ -1,16 +1,25 @@
<script> <script>
import PreviewCardSmall from "../../records/PreviewCardSmall.svelte"; import PreviewCardSmall from "../../records/PreviewCardSmall.svelte";
export let queryRecord; export let record;
export let field; export let field;
$: recordEdges = queryRecord?._children[field.name]; export let schemas;
export let graph;
$: recordEdges =
graph.edges
?.filter((ed) => ed.field === field.name && ed.source === record.id)
.map((edge) => {
return graph.records.find((r) => r.id === edge.target);
})
.filter((record) => (!record ? false : true)) ?? [];
</script> </script>
<div class="references"> <div class="references">
{#each recordEdges as recordEdge} {#each recordEdges as recordEdge}
<span class="mr-3"> <span class="mr-3">
<PreviewCardSmall record={recordEdge}/> <PreviewCardSmall {schemas} {graph} record={recordEdge}/>
</span> </span>
{/each} {/each}
</div> </div>
@@ -10,7 +10,7 @@
export let value; export let value;
export let inModal; export let inModal;
export let modalUrl; export let modalUrl;
export let records export let graph;
let filter = { let filter = {
label: "", label: "",
@@ -51,13 +51,13 @@
} }
const filterRecord = extractFilterRecord(records, value); const filterRecord = extractFilterRecord(graph, value);
function extractFilterRecord(records, value) { function extractFilterRecord(graph, value) {
if (!filter.isReference) { if (!filter.isReference) {
return null; return null;
} }
return records.find(r => r.id === value); return graph.records.find(r => r.id === value);
} }
function removeFilter(k) { function removeFilter(k) {
@@ -76,7 +76,7 @@
<span class="applied-filter d-inline-block border border-primary rounded lx-small-text me-1 px-2 py-1"> <span class="applied-filter d-inline-block border border-primary rounded lx-small-text me-1 px-2 py-1">
<div class="d-flex align-items-center justify-content-center"> <div class="d-flex align-items-center justify-content-center">
{#if filter.isReference && filterRecord} {#if filter.isReference && filterRecord}
{filter.label} is {previewTitle(filterRecord)} {filter.label} is {previewTitle(channel.schemas, filterRecord)}
{:else} {:else}
{filter.label} {operators.find((o) => o.name === filter.operator)?.symbol ?? ""} {value} {filter.label} {operators.find((o) => o.name === filter.operator)?.symbol ?? ""} {value}
{/if} {/if}
@@ -62,7 +62,7 @@
on:keypress={(e) => apply(e, option)} on:keypress={(e) => apply(e, option)}
> >
<span class="dropdown-item"> <span class="dropdown-item">
{previewTitle( option)} {previewTitle(channel.schemas, option)}
</span> </span>
</div> </div>
+3 -2
View File
@@ -18,6 +18,7 @@
export let modalUrl; export let modalUrl;
export let isWritable; export let isWritable;
export let records; export let records;
export let graph;
export let systemFields = []; export let systemFields = [];
// export let visibleFields = []; // export let visibleFields = [];
@@ -36,7 +37,7 @@
if (inModal) { if (inModal) {
dispatch("refresh", url); dispatch("refresh", url);
} else { } else {
window.location.href = url; window.location = url;
} }
} }
@@ -151,7 +152,7 @@
value={v} value={v}
{inModal} {inModal}
{modalUrl} {modalUrl}
{records} {graph}
on:refresh on:refresh
/> />
{/each} {/each}
-54
View File
@@ -1,54 +0,0 @@
<script>
import {createEventDispatcher, getContext} from "svelte";
const dispatch = createEventDispatcher();
const channel = getContext("channel");
let isOpen = false;
export function open() {
isOpen = true;
}
</script>
<div
class="modal fade show"
tabindex="-1"
class:d-block={isOpen}
aria-modal="true"
role="dialog"
style="background: rgba(100,100,100,.6);"
>
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<button
on:click|preventDefault={(e) => (isOpen = false)}
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
/>
</div>
<div class="modal-body">
</div>
</div>
</div>
</div>
<style>
.modal-dialog {
width: auto;
max-width: 100%;
}
.modal-content {
margin: 40px auto;
width: auto;
height: 100%;
}
</style>
+8 -4
View File
@@ -1,9 +1,13 @@
export function sortByField(from, to, queryRecords, fieldName) {
export function sortByField(from, to, edges, fieldName, references) {
if (from === to) { if (from === to) {
return queryRecords; return edges;
} }
let edgesTosort = queryRecords?.filter((qr) => qr.edge.field === fieldName && qr.edge.depth === 1 ) ?? []; let referenceIds = references.map(r => r.id);
let remainingEdge = queryRecords?.filter((qr) => !(qr.edge.field === fieldName && qr.edge.depth === 1)) ?? []; 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)) ?? [];
edgesTosort = array_move(edgesTosort,from, to); edgesTosort = array_move(edgesTosort,from, to);
return [...remainingEdge, ...edgesTosort]; return [...remainingEdge, ...edgesTosort];
+1 -1
View File
@@ -23,7 +23,7 @@
<div class="wrapper-normal transparent"> <div class="wrapper-normal transparent">
<h3 class="header-small mb-4 ">Latest Content changes</h3> <h3 class="header-small mb-4 mt-5">Latest Content changes</h3>
{#if records.length > 0} {#if records.length > 0}
<div class="lx-card mb-4"> <div class="lx-card mb-4">
<div class="lx-table p-0"> <div class="lx-table p-0">
+1 -1
View File
@@ -4,7 +4,7 @@
export let sortableClass = ""; export let sortableClass = "";
// export let handle; // export let handle;
export let isTable = false; export let isTable = false;
export let sortableInstance = null; export let sortableInstance;
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
let sortableContainer; let sortableContainer;
-65
View File
@@ -1,65 +0,0 @@
<script>
import File from "./includes/File.svelte";
import {previewTitle} from "../records/Preview.js";
import {getContext} from "svelte";
import Status from "../records/Status.svelte";
const channel = getContext("channel");
export let record;
export let edge = null;
let schema = channel.schemas.find(s => s.name === record.schema);
let types = ["inline", "card"];
export let type = "inline";
if (!types.includes(type)) {
console.error("unknown preview type")
}
export let editable = false;
</script>
<div class="preview-card">
{#if edge?.data}
<div class="preview-card-edge">Edge Data</div>
{/if}
<div class="d-flex column-gap-3">
{#if record._file}
<div>
<File {record}/>
</div>
{/if}
<div class="d-flex flex-md-column " style="line-height: 22px">
<a class="text-decoration-none" target="_blank" href="{channel.lucentUrl}/records/{record.id}">{previewTitle(record)}</a>
<span class="d-flex gap-1 text-muted">
{#if record.status === "draft"}
<Status status={record.status}/>
{/if}
{schema.label}
</span>
</div>
</div>
</div>
<style>
.preview-card {
position: relative
}
.preview-card-edge {
position: absolute;
top: -28px;
background: #fff;
padding: 0px 5px;
border: 1px solid #ccc;
border-radius: 7px;
font-size: 14px;
}
</style>
<!--{#if record._file && type === "inline"}-->
<!--&lt;!&ndash; <FilePreviewInline {record} {edge} {editable}/>&ndash;&gt;-->
<!--{:else if record._file && type === "card"}-->
<!-- <FilePreviewCard {record} {edge} {editable}/>-->
<!--{:else if type === "inline"}-->
<!--&lt;!&ndash; <DocPreviewCard {record} {edge} {editable}/>&ndash;&gt;-->
<!--{:else if type === "card"}-->
<!--&lt;!&ndash; <DocPreviewCard {record} {edge} {editable}/>&ndash;&gt;-->
<!--{/if}-->
@@ -1,60 +0,0 @@
<script>
import {imgurl} from "../../files/imageserver.js";
import {getContext} from "svelte";
import Icon from "../../common/Icon.svelte";
const channel = getContext("channel");
export let record;
export let size = "tiny";
let imageSide;
let fileSide;
let fontSize;
if (size === "large") {
imageSide = 256;
fileSide = 32;
fontSize = "20";
} else if (size === "medium") {
imageSide = 128;
fileSide = 12;
fontSize = "17";
} else if (size === "small") {
imageSide = 64;
fileSide = 12;
fontSize = "15";
} else if (size === "tiny") {
imageSide = 42;
fileSide = 12;
fontSize = "13";
}
</script>
{#if record}
{#if record._file.mime.startsWith("image")}
<!-- href={imgurl(record)} -->
<a
href="{channel.lucentUrl}/records/{record.id}"
title={record._file.path}
class="d-flex align-items-center justify-content-center "
style="width:{imageSide}px;height:{imageSide}px"
>
<img
class="rounded w-100"
src={imgurl(record)}
alt={record._file.path}
/>
</a>
{:else}
<a
href="{channel.lucentUrl}/records/{record.id}"
title={record._file.path}
class="btn btn-outline-primary btn-sm d-flex align-items-center justify-content-center"
style="width:{imageSide}px;height:{imageSide}px"
>
<Icon icon="file" width={fileSide} height={fileSide}/>
<span class="ms-2" style="font-size:{fontSize}px"
>.{record._file.path.split(".").pop()}</span
>
</a>
{/if}
{/if}
+19 -1
View File
@@ -1,5 +1,7 @@
<script> <script>
export let schema; export let schema;
export let isCreateMode;
export let active = ""; export let active = "";
let tabs = schema.groups?.map((group) => { let tabs = schema.groups?.map((group) => {
@@ -9,13 +11,29 @@
label: "Main", label: "Main",
name: "", name: "",
}; };
let graphTab = {
label: "Graph",
name: "_graph",
};
if (isCreateMode) {
tabs = [mainTab, ...tabs]; tabs = [mainTab, ...tabs];
} else {
tabs = [mainTab, ...tabs, graphTab];
}
function showGraph(e) {
e.preventDefault();
active = "_graph";
}
function changeTab(e, tabName) { function changeTab(e, tabName) {
e.preventDefault(); e.preventDefault();
if (tabName == "_graph") {
showGraph(e);
} else {
active = tabName; active = tabName;
} }
}
</script> </script>
{#if tabs.length > 1} {#if tabs.length > 1}
+162 -36
View File
@@ -1,60 +1,131 @@
<script> <script>
import {getContext} from "svelte"; import {afterUpdate, getContext, onMount} from "svelte";
import {isEqual} from "lodash";
import Manager from "./Manager.svelte"; import Manager from "./Manager.svelte";
import EditHeader from "./EditHeader.svelte"
import StatusSelect from "./StatusSelect.svelte"
import FilePreview from "./FilePreview.svelte" import FilePreview from "./FilePreview.svelte"
import Form from "./form/Form.svelte"; import ContentTabs from "./ContentTabs.svelte"
import axios from "axios"; import FormField from "./FormField.svelte"
import Graph from "./Graph.svelte"
import Info from "./Info.svelte"
import ErrorAlert from "../common/ErrorAlert.svelte"
const channel = getContext("channel"); const channel = getContext("channel");
export let schema; export let schema;
export let record; export let record;
export let graph = []; export let graph = {
records: [],
edges: []
};
export let recordHistory; export let recordHistory;
export let isCreateMode; export let isCreateMode;
// export let isWritable = false; export let isWritable = false;
// export let users; export let users;
let originalContent;
let activeContentTab = "";
$: hasUnsavedData = false;
$: validationErrors = null; $: validationErrors = null;
$: errorMessage = null; $: errorMessage = validationErrors
? `Record submission failed. ${
Object.entries(validationErrors).length
} error(s)`
: null;
let form; let activeFields = schema.fields.filter(
(f) => f.name !== "id"
);
onMount(() => {
setOriginalContent();
});
function setOriginalContent() {
originalContent = {
data: JSON.parse(JSON.stringify(record.data)),
schema: record.schema,
status: record.status,
_sys: JSON.parse(JSON.stringify(record._sys)),
_file: JSON.parse(JSON.stringify(record._file)),
edges: JSON.parse(JSON.stringify(graph.edges)),
};
}
afterUpdate(() => {
hasUnsavedData = checkUnsavedData();
});
function beforeUnload(e) {
// Cancel the event as stated by the standard.
// e.preventDefault();
// console.log(hasUnsavedData);
if (hasUnsavedData) {
return (e.returnValue =
"You have unsaved changes. Are you sure you want to exit?");
}
// Chrome requires returnValue to be set.
// e.returnValue = "";
delete e["returnValue"];
// more compatibility
// return true;
return "...";
}
function checkUnsavedData() {
if (isCreateMode) {
return false;
}
return !isEqual(originalContent, {
data: record.data,
schema: record.schema,
status: record.status,
_sys: record._sys,
_file: record._file,
edges: graph.edges,
});
}
function save(e) { function save(e) {
e.preventDefault(); e.preventDefault();
let status = e.detail.status
console.log("SAVE: Attempt"); console.log("SAVE: Attempt");
validationErrors = null; validationErrors = null;
errorMessage = "";
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
if (!hasUnsavedData && !isCreateMode) {
resolve(null);
return;
}
if (!record) {
resolve(null);
return;
}
// remove trashed edges // remove trashed edges
let replaceEdges = graph graph.edges = graph.edges?.filter((edge) => !edge._isTrashed && edge.source === record.id);
.map((queryRecord) => queryRecord.edge)
.filter((edge) => !edge._isTrashed && edge.source === record.id);
axios axios
.post(channel.lucentUrl + "/records", { .post(channel.lucentUrl + "/records", {
schemaName: record.schema, record: record,
updateEdges: true, edges: graph.edges,
id: record.id,
data: record.data,
edges: replaceEdges,
status: status,
isCreateMode: isCreateMode, isCreateMode: isCreateMode,
}) })
.then(function (response) { .then(function (response) {
console.log("SAVE: SAVED"); console.log("SAVE: SAVED");
if (isCreateMode) { if (isCreateMode) {
window.location.href = channel.lucentUrl + "/records/" + record.id; window.location = channel.lucentUrl + "/records/" + record.id;
} else { } else {
record = response.data.record ?? null; record = response.data.records[0] ?? null;
if (!record) { if (!record) {
// means trashed // means trashed
hasUnsavedData = false;
window.location = channel.lucentUrl; window.location = channel.lucentUrl;
return; return;
} }
graph = [...response.data.graph]; graph = response.data;
form.setOriginalData(); setOriginalContent();
} }
resolve(null); resolve(null);
@@ -66,36 +137,91 @@
errorMessage = error.response.data.error; errorMessage = error.response.data.error;
} else { } else {
validationErrors = error.response.data.error; validationErrors = error.response.data.error;
// console.log(validationErrors) console.log(validationErrors)
} }
} }
resolve(null); resolve(null);
// msgSuccess = null;
// msgError = error.response.data.error;
// submitted = false;
}); });
}); });
} }
</script> </script>
<svelte:window on:beforeunload={beforeUnload}/>
<div class="wrapper-normal transparent"> <div class="wrapper-normal transparent">
<Manager managerRecords={recordHistory} {graph}/> <Manager managerRecords={recordHistory} {graph}/>
<EditHeader {schema} {record} {isCreateMode} {graph} bind:activeContentTab/>
<FilePreview {record} {schema}/> {#if !["_graph", "_info"].includes(activeContentTab) && isWritable}
<div class="shadow-lg "
style="position:fixed;bottom:0;left:0px;width:100%;background: rgb(206, 223, 210);z-index:1050"
>
<div
class="d-flex mt-3 mb-3 align-items-center justify-content-center"
>
<StatusSelect bind:status={record.status} {record} {schema}/>
{#if isCreateMode}
<button
class="ms-2 btn btn-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="ms-2 btn btn-primary btn-spinner"
on:click={save}
>
<span
class="spinner-border spinner-border-sm"
role="status"
aria-hidden="true"
/>
Save
</button>
{/if}
</div>
</div>
{/if}
<ErrorAlert message={errorMessage}/>
<div class=" mt-4" style="margin-bottom:150px"> <div class=" mt-4" style="margin-bottom:150px">
<Form <ContentTabs
bind:this={form} {schema}
data={record.data} {isCreateMode}
status={record.status} bind:active={activeContentTab}
bind:graph />
{#if !["_graph", "_info"].includes(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} {schema}
{record} {record}
{isCreateMode}
{errorMessage}
{validationErrors} {validationErrors}
on:save={save} {isCreateMode}
/> />
<!-- <Graph {graph} {record}/>--> {/if}
<!-- <Info {record} {graph} {users} {schema}/>--> {/each}
<!-- </fieldset> -->
{:else if activeContentTab === "_graph"}
<Graph {graph} {record}/>
{:else if activeContentTab === "_info"}
<Info {record} {graph} {users} {schema}/>
{/if}
</div> </div>
</div> </div>
+12 -16
View File
@@ -2,25 +2,25 @@
import {getContext} from "svelte"; import {getContext} from "svelte";
import Icon from "../common/Icon.svelte"; import Icon from "../common/Icon.svelte";
import {previewTitle} from "./Preview"; import {previewTitle} from "./Preview";
import axios from "axios";
const channel = getContext("channel"); const channel = getContext("channel");
export let schema; export let schema;
export let graph;
export let record; export let record;
export let title;
export let isCreateMode; export let isCreateMode;
export let activeContentTab;
function clone(e) { function clone(e) {
e.preventDefault(); e.preventDefault();
axios.post(channel.lucentUrl + "/records/clone/" + record.id).then(response => { axios.post(channel.lucentUrl + "/records/clone/" + record.id).then(response => {
window.location.href = channel.lucentUrl + "/records/" + response.data.id; window.location = channel.lucentUrl + "/records/" + response.data.id;
}).catch(error => { }).catch(error => {
}); });
} }
</script> </script>
<h3 class="header-normal mb-0"> <h3 class="header-normal mt-5 mb-0">
<a <a
class="text-muted d-block text-decoration-none fs-6 mb-1" class="text-muted d-block text-decoration-none fs-6 mb-1"
href="{channel.lucentUrl}/content/{schema.name}" href="{channel.lucentUrl}/content/{schema.name}"
@@ -29,16 +29,12 @@
<span class="text-dark d-block"> <span class="text-dark d-block">
{#if !isCreateMode} {#if !isCreateMode}
{#if record} {previewTitle(channel.schemas, record, graph)}
{previewTitle(record)}
{:else}
{ title}
{/if}
{:else} {:else}
New Record New Record
{/if} {/if}
</span> </span>
{#if !isCreateMode && !!record} {#if !isCreateMode}
<div class="dropdown d-inline-block"> <div class="dropdown d-inline-block">
<button <button
class="btn btn-link btn-sm" class="btn btn-link btn-sm"
@@ -65,12 +61,12 @@
Clone Clone
</a> </a>
{/if} {/if}
<!-- <a--> <a
<!-- on:click|preventDefault={(e) =>--> on:click|preventDefault={(e) =>
<!-- (activeContentTab = "_info")}--> (activeContentTab = "_info")}
<!-- class="dropdown-item"--> class="dropdown-item"
<!-- href="{channel.lucentUrl}">Revisions</a--> href="{channel.lucentUrl}">Revisions</a
<!-- >--> >
</div> </div>
</div> </div>
{/if} {/if}
+28 -18
View File
@@ -1,23 +1,24 @@
<script> <script>
import Text from "./form/fields/Text.svelte"; import Text from "./elements/Text.svelte";
import Slug from "./form/fields/Slug.svelte"; import Slug from "./elements/Slug.svelte";
import Reference from "./elements/Reference.svelte"; import Reference from "./elements/Reference.svelte";
import ReferenceInline from "./elements/ReferenceInline.svelte"; import ReferenceInline from "./elements/ReferenceInline.svelte";
import Block from "./block/Block.svelte"; import Block from "./block/Block.svelte";
import Color from "./form/fields/Color.svelte"; import Color from "./elements/Color.svelte";
import Checkbox from "./form/fields/Checkbox.svelte"; import Checkbox from "./elements/Checkbox.svelte";
import Number from "./form/fields/Number.svelte"; import Number from "./elements/Number.svelte";
import Date from "./form/fields/Date.svelte"; import Url from "./elements/Url.svelte";
import UUID from "./form/fields/UUID.svelte"; import Date from "./elements/Date.svelte";
import File from "./form/references/File.svelte"; import UUID from "./elements/UUID.svelte";
import Textarea from "./form/fields/Textarea.svelte"; import File from "./elements/File.svelte";
import Datetime from "./form/fields/Datetime.svelte"; import Textarea from "./elements/Textarea.svelte";
import RichEditor from "./form/fields/RichEditor.svelte"; import Datetime from "./elements/Datetime.svelte";
import Json from "./form/fields/JSON.svelte"; import RichEditor from "./elements/RichEditor.svelte";
import Markdown from "./form/fields/Markdown.svelte"; import Json from "./elements/JSON.svelte";
import FieldHeader from "./form/FieldHeader.svelte"; import Markdown from "./elements/Markdown.svelte";
import FieldHeader from "./elements/FieldHeader.svelte";
import ReferenceTable from "./elements/ReferenceTable.svelte"; import ReferenceTable from "./elements/ReferenceTable.svelte";
import ReferenceTags from "./form/references/ReferenceTags.svelte"; import ReferenceTags from "./elements/ReferenceTags.svelte";
const formElements = { const formElements = {
text: Text, text: Text,
@@ -27,6 +28,7 @@
color: Color, color: Color,
checkbox: Checkbox, checkbox: Checkbox,
number: Number, number: Number,
url: Url,
date: Date, date: Date,
datetime: Datetime, datetime: Datetime,
uuid: UUID, uuid: UUID,
@@ -46,7 +48,7 @@
</script> </script>
<div class="card editor-field"> <div class="card editor-field">
<FieldHeader {field} {id}/> <FieldHeader {schema} {field} {id}/>
{#if field.info.name === "reference" && field.layout === "inline"} {#if field.info.name === "reference" && field.layout === "inline"}
<ReferenceInline <ReferenceInline
bind:graph bind:graph
@@ -70,8 +72,16 @@
{field} {field}
{validationErrors} {validationErrors}
/> />
{:else if ["reference","file"].includes(field.info.name)} {:else if field.info.name === "reference"}
<File bind:graph {record} {field} /> <Reference
bind:graph
{id}
{record}
{field}
{validationErrors}
/>
{:else if field.info.name === "file"}
<File bind:graph {record} {field} {validationErrors}/>
{:else if field.info.name === "block"} {:else if field.info.name === "block"}
<Block <Block
bind:graph bind:graph
+1 -1
View File
@@ -5,7 +5,7 @@
import FormField from "./FormField.svelte"; import FormField from "./FormField.svelte";
import FilePreview from "./FilePreview.svelte"; import FilePreview from "./FilePreview.svelte";
import ContentTabs from "./ContentTabs.svelte"; import ContentTabs from "./ContentTabs.svelte";
import StatusSelect from "./form/StatusSelect.svelte"; import StatusSelect from "./StatusSelect.svelte";
import ErrorAlert from "../common/ErrorAlert.svelte"; import ErrorAlert from "../common/ErrorAlert.svelte";
const channel = getContext("channel"); const channel = getContext("channel");
+21 -26
View File
@@ -1,17 +1,31 @@
import Mustache from "mustache"; import Mustache from "mustache";
import {stripHtml} from "../../helpers"; import {stripHtml} from "../../helpers";
import {getContext} from "svelte";
export function previewTitle(record) { export function previewTitle(schemas, record, graph) {
const channel = getContext("channel"); let schema = schemas.find((aSchema) => aSchema.name === record?.schema);
let schema = channel.schemas.find((aSchema) => aSchema.name === record?.schema);
if (!schema?.titleTemplate) { if (!schema?.titleTemplate) {
return noTemplate(schema, record); return noTemplate(schema, record);
} }
let recordData = record.data;
let template = Mustache.parse(schema.titleTemplate); let template = Mustache.parse(schema.titleTemplate);
let render = Mustache.render(schema.titleTemplate, record.data);
let referencePreviews = template
.filter(segment => segment[0] === "name") // keep only template tags
.map((segment) => segment[1]) // map to fieldNames
.filter(fieldName => { // keep only references
let schemaField = schema.fields.find(f => f.name === fieldName)
return schemaField?.info.name === "reference";
}).reduce((carry, field) => { // map to records
let edge = graph.edges.find(edge => edge.source === record.id && edge.field === field)
let referenceRecord = graph.records.find(rec => rec.id === edge?.target)
carry[field] = previewTitle(schemas, referenceRecord, graph);
return carry;
}, {});
recordData = {...recordData, ...referencePreviews}
let render = Mustache.render(schema.titleTemplate, recordData);
if (!render || render === "") { if (!render || render === "") {
return noTemplate(schema, record); return noTemplate(schema, record);
@@ -20,26 +34,6 @@ export function previewTitle(record) {
return stripHtml(render.slice(0, 300)); return stripHtml(render.slice(0, 300));
} }
export function previewEdgeTitle(edge) {
const channel = getContext("channel");
let edgeSchemaName = channel.schemas
.find((aSchema) => aSchema.name === edge?.sourceSchema)
.fields.find(f => f.name === edge.field).data;
let schema = channel.schemas.find((aSchema) => aSchema.name === edgeSchemaName);
if (!schema?.titleTemplate) {
return noTemplate(schema, edge);
}
let template = Mustache.parse(schema.titleTemplate);
let render = Mustache.render(schema.titleTemplate, edge.data);
if (!render || render === "") {
return noTemplate(schema, edge);
}
return stripHtml(render.slice(0, 300));
}
function noTemplate(schema, record) { function noTemplate(schema, record) {
if (schema?.type === "files") { if (schema?.type === "files") {
return record._file.path; return record._file.path;
@@ -48,7 +42,8 @@ function noTemplate(schema, record) {
let title = stripHtml( let title = stripHtml(
record?.data[schema.fields.filter((f) => f.info.name === "text")[0]?.name] record?.data[schema.fields.filter((f) => f.info.name === "text")[0]?.name]
).slice(0, 300); ).slice(0, 300);
if (title === "") {
if(title == ""){
return "Untitled"; return "Untitled";
} }
+30 -48
View File
@@ -2,92 +2,74 @@
import Icon from "../common/Icon.svelte"; import Icon from "../common/Icon.svelte";
import { getContext, createEventDispatcher } from "svelte"; import { getContext, createEventDispatcher } from "svelte";
import {previewEdgeTitle, previewTitle} from "./Preview"; import Preview from "../files/Preview.svelte";
import { previewTitle } from "./Preview";
import Status from "./Status.svelte"; import Status from "./Status.svelte";
import Preview from "../newPreview/Preview.svelte";
import EdgeData from "./form/references/EdgeData.svelte";
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
const channel = getContext("channel"); const channel = getContext("channel");
export let graph;
export let record; export let record;
export let field;
export let edge = null;
export let editable = false;
export let classes = ""; export let classes = "";
export let hasDelete = false; export let hasDelete = false;
let edgeData;
let schema = channel.schemas.find((aschema) => aschema.name === record.schema); let schema = channel.schemas.find((aschema) => aschema.name === record.schema);
let cardTitle = previewTitle(record); let cardTitle = previewTitle(channel.schemas, record, graph);
function remove(e) { function remove(e) {
e.preventDefault(); e.preventDefault();
dispatch("remove", record.id); dispatch("remove", record.id);
} }
function edit(e) {
e.preventDefault();
edgeData.openEdit();
}
</script> </script>
<div class="d-flex gap-2">
{#if editable}
<div <div
class="card mb-2 bg-light w-50 "
class="card mb-2 bg-light {classes}"
style="border-color:{schema.color ?? '#ccc'}; border-width: 1px;" style="border-color:{schema.color ?? '#ccc'}; border-width: 1px;"
> >
<div class="card-body"> <div class="card-body d-flex">
<span class="text-muted d-block">Relation Data</span> {#if schema.type === "files"}
{previewEdgeTitle(edge)} <div style="max-width:94px;margin-right:15px">
<div class="position-absolute d-flex end-0" style="top:5px"> <Preview {record} size="small" />
<button
class="trash-button text-dark btn btn-sm btn-link"
on:click={edit}
>
<Icon icon="pencil"/>
</button>
</div> </div>
</div>
</div>
<EdgeData bind:this={edgeData} {record} {field} bind:edge/>
{/if} {/if}
<div class="overflow-hidden">
<div <a
class="card mb-2 bg-light w-100 {classes}" class="title-link m-0 fs-5 text-decoration-none text-dark d-block"
style="border-color:{schema.color ?? '#ccc'}; border-width: 1px;" href="{channel.lucentUrl}/records/{record.id}"
title={cardTitle}
> >
<div class="card-body"> {cardTitle}
<Preview {record} type="card"/> </a>
<small class="text-muted">
{schema.label}
</small>
<small class="text-muted">
{#if record.status === "draft"}
<Status status={record.status} />
{/if}
</small>
</div>
</div> </div>
{#if hasDelete} {#if hasDelete}
<div class="position-absolute d-flex end-0" style="top:5px"> <div class="position-absolute end-0" style="top:5px">
<button <button
class="trash-button text-dark btn btn-sm btn-link" class="trash-button text-dark btn btn-sm btn-link"
on:click={remove} on:click={remove}
> ><Icon icon="trash-can" />
<Icon icon="trash-can"/>
</button> </button>
</div> </div>
{/if} {/if}
</div> </div>
</div>
<style> <style>
.card .trash-button { .card .trash-button {
display: none; display: none;
} }
.card:hover .trash-button { .card:hover .trash-button {
display: block; display: block;
} }
.title-link { .title-link {
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
@@ -4,9 +4,10 @@
const channel = getContext("channel"); const channel = getContext("channel");
export let record; export let record;
export let graph;
$: schema = channel.schemas.find((aschema) => aschema.name === record.schema); $: schema = channel.schemas.find((aschema) => aschema.name === record.schema);
$: title = previewTitle( record); $: title = previewTitle(channel.schemas, record, graph);
</script> </script>
{#if record?.data} {#if record?.data}
@@ -0,0 +1,57 @@
<script>
import {getContext} from "svelte";
import {getStatus, getStatusList} from "./StatusText";
const channel = getContext("channel");
export let status = "draft";
export let record;
export let schema;
let dropdown;
$: currentStatus = getStatus(status);
const statusList = Object.values(getStatusList());
function updateStatus(e, statusValue) {
// e.preventDefault();
status = statusValue;
dropdown.click();
}
</script>
<!-- Example split danger button -->
<div class="d-flex justify-content-between">
<div class="btn-group dropup">
<button type="button" class="btn btn-{currentStatus.bg}"
>{currentStatus.text}</button
>
<button
bind:this={dropdown}
type="button"
class="btn btn-{currentStatus.bg} dropdown-toggle dropdown-toggle-split"
data-bs-toggle="dropdown"
aria-expanded="false"
>
<span class="visually-hidden">Toggle Dropdown</span>
</button>
<div class="dropdown-menu">
<div class="dropdown-header">Change status to</div>
{#each statusList as astatus}
{#if astatus.value !== status}
<button
type="button"
class="dropdown-item my-2 rounded w-100 bg-{astatus.bg} text-{astatus.color}"
on:click={(e) => updateStatus(e, astatus.value)}
>
{astatus.text}
</button>
{/if}
{/each}
</div>
</div>
{#if channel.previewTarget}
<a href="{channel.previewTargetUrl}?schema={schema.name}&id={record.id}" target="_blank" class="btn btn-info ms-3">
Preview
</a>
{/if}
</div>
@@ -4,8 +4,8 @@
import {sortByField} from "../../../edges/sortEdges"; import {sortByField} from "../../../edges/sortEdges";
import ReferenceInlineButtons from "../../elements/ReferenceInlineButtons.svelte" import ReferenceInlineButtons from "../../elements/ReferenceInlineButtons.svelte"
import Sortable from "../../../libs/Sortable.svelte"; import Sortable from "../../../libs/Sortable.svelte";
import {insertEdges} from "../../form/references/reference.js"; import {insertEdges} from "../../elements/reference";
import BrowseModal from "../../form/references/BrowseModal.svelte"; import BrowseModal from "../../elements/BrowseModal.svelte";
const channel = getContext("channel"); const channel = getContext("channel");
@@ -41,7 +41,7 @@
} }
function reorder(e) { function reorder(e) {
graph.edges = sortByField(e.detail.source, e.detail.target, graph.edges, blockFieldName); graph.edges = sortByField(e.detail.source, e.detail.target, graph.edges, blockFieldName, references);
} }
function insert(e) { function insert(e) {
@@ -4,7 +4,7 @@
import {sortByField} from "../../../edges/sortEdges"; import {sortByField} from "../../../edges/sortEdges";
import ReferenceInlineButtons from "../../elements/ReferenceInlineButtons.svelte" import ReferenceInlineButtons from "../../elements/ReferenceInlineButtons.svelte"
import Sortable from "../../../libs/Sortable.svelte"; import Sortable from "../../../libs/Sortable.svelte";
import {insertEdges} from "../../form/references/reference.js"; import {insertEdges} from "../../elements/reference";
const channel = getContext("channel"); const channel = getContext("channel");
@@ -35,7 +35,7 @@
} }
function reorder(e) { function reorder(e) {
graph.edges = sortByField(e.detail.source, e.detail.target, graph.edges, blockFieldName); graph.edges = sortByField(e.detail.source, e.detail.target, graph.edges, blockFieldName, references);
} }
function insert(e) { function insert(e) {
-34
View File
@@ -1,34 +0,0 @@
import { deepEqual } from 'fast-equals';
export function isEqual(obj1, obj2) {
return deepEqual(obj1, obj2);
// if (obj1 === obj2) return true;
//
// if (Array.isArray(obj1) && Array.isArray(obj2)) {
//
// if(obj1.length !== obj2.length) return false;
//
// return obj1.every((elem, index) => {
// return isEqual(elem, obj2[index]);
// })
//
//
// }
//
// if(typeof obj1 === "object" && typeof obj2 === "object" && obj1 !== null && obj2 !== null) {
// if(Array.isArray(obj1) || Array.isArray(obj2)) return false;
//
// const keys1 = Object.keys(obj1)
// const keys2 = Object.keys(obj2)
//
// if(keys1.length !== keys2.length || !keys1.every(key => keys2.includes(key))) return false;
//
// for(let key in obj1) {
// if (!isEqual(obj1[key], obj2[key])) { return false; }
// }
//
// return true;
//
// }
//
// return false;
}
@@ -1,6 +1,6 @@
<script> <script>
import {createEventDispatcher, getContext} from "svelte"; import {createEventDispatcher, getContext} from "svelte";
import Index from "../../../content/Index.svelte"; import Index from "../../content/Index.svelte";
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
const channel = getContext("channel"); const channel = getContext("channel");
@@ -1,5 +1,5 @@
<script> <script>
import { getErrorMessage } from "../form.js"; import { getErrorMessage } from "./errorMessage";
export let id; export let id;
export let field; export let field;
export let value; export let value;
@@ -1,5 +1,5 @@
<script> <script>
import { getErrorMessage } from "../form.js"; import { getErrorMessage } from "./errorMessage";
export let field; export let field;
export let value; export let value;
export let isCreateMode; export let isCreateMode;
@@ -1,7 +1,7 @@
<script> <script>
import {getContext} from "svelte"; import {getContext} from "svelte";
import {debounce} from "lodash"; import {debounce} from "lodash";
import {previewTitle} from "../../Preview.js"; import {previewTitle} from "../Preview";
const channel = getContext("channel"); const channel = getContext("channel");
export let field; export let field;
@@ -4,8 +4,8 @@
import flatpickr from "flatpickr"; import flatpickr from "flatpickr";
import "flatpickr/dist/flatpickr.css"; import "flatpickr/dist/flatpickr.css";
import "flatpickr/dist/themes/light.css"; import "flatpickr/dist/themes/light.css";
import { getErrorMessage } from "../form.js"; import {getErrorMessage} from "./errorMessage";
import Icon from "../../../common/Icon.svelte"; import Icon from "../../common/Icon.svelte";
export let field; export let field;
export let value; export let value;
@@ -4,8 +4,8 @@
import flatpickr from "flatpickr"; import flatpickr from "flatpickr";
import "flatpickr/dist/flatpickr.css"; import "flatpickr/dist/flatpickr.css";
import "flatpickr/dist/themes/light.css"; import "flatpickr/dist/themes/light.css";
import { getErrorMessage } from "../form.js"; import {getErrorMessage} from "./errorMessage";
import Icon from "../../../common/Icon.svelte"; import Icon from "../../common/Icon.svelte";
export let field; export let field;
export let value; export let value;
@@ -1,18 +1,22 @@
<script> <script>
import {getContext} from "svelte"; import {getContext} from "svelte";
import PreviewCard from "../../PreviewCard.svelte"; import {uniqBy} from "lodash";
import Sortable from "../../../libs/Sortable.svelte"; import {sortByField} from "../../edges/sortEdges";
import PreviewCard from "../PreviewCard.svelte";
import Sortable from "../../libs/Sortable.svelte";
import BrowseModal from "./BrowseModal.svelte"; import BrowseModal from "./BrowseModal.svelte";
import {insertEdges} from "./reference.js";
import {sortByField} from "../../../edges/sortEdges.js";
const channel = getContext("channel"); const channel = getContext("channel");
export let field; export let field;
export let record; export let record;
export let graph; export let graph
let browseModal; let browseModal;
$: currentReferences = graph.filter((queryRecord)=> field.name === queryRecord.edge.field) ?? []; $: 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 collections = channel.schemas.filter((aschema) => let collections = channel.schemas.filter((aschema) =>
field.collections.includes(aschema.name) field.collections.includes(aschema.name)
@@ -20,8 +24,8 @@
function removeReference(e) { function removeReference(e) {
e.preventDefault(); e.preventDefault();
graph = graph.filter( graph.edges = graph.edges.filter(
(queryRecord) => !(queryRecord.edge.target === e.detail && queryRecord.edge.field === field.name) (edge) => !(edge.target === e.detail && edge.field === field.name)
); );
} }
@@ -31,17 +35,34 @@
} }
async function reorder(e) { async function reorder(e) {
graph = await sortByField(e.detail.source, e.detail.target, graph, field.name);
graph.edges = await sortByField(e.detail.source, e.detail.target, graph.edges, field.name, references);
} }
function insert(e) { function insert(e) {
e.preventDefault(); e.preventDefault();
browseModal.close(); browseModal.close();
const recordsToInsert = e.detail.records; const recordsToInsert = e.detail.records;
const action = e.detail.action; const action = e.detail.action;
graph = insertEdges(graph, record, recordsToInsert, field.name, action); let newEdges = recordsToInsert.map((r) => {
return {
target: r.id,
source: record.id,
sourceSchema: record.schema,
targetSchema: r.schema,
field: field.name,
rank: ""
};
});
let replacedEdges = graph.edges ?? [];
if (action === "replace") {
replacedEdges = replacedEdges.filter((e) => e.field !== field.name);
}
graph.records = uniqBy([...graph.records, ...recordsToInsert], (r) => r.id);
graph.edges = uniqBy([...replacedEdges, ...newEdges], (e) => e.target + e.field);
} }
</script> </script>
@@ -79,16 +100,14 @@
</div> </div>
{/if} {/if}
</div> </div>
{#if currentReferences.length > 0} {#if references.length > 0}
<Sortable sortableClass="mt-3" on:update={reorder}> <Sortable sortableClass="row row-cols-3 mt-3" on:update={reorder}>
{#each currentReferences as reference (reference.record.id)} {#each references as reference (reference.id)}
<div class="mb-1"> <div class="col mb-3">
<PreviewCard <PreviewCard
record={reference.record} classes="h-100"
record={reference}
hasDelete={true} hasDelete={true}
editable={!!field?.data}
{field}
bind:edge={reference.edge}
on:remove={removeReference} on:remove={removeReference}
/> />
</div> </div>
@@ -1,6 +1,6 @@
<script> <script>
import Codemirror from "../../../libs/Codemirror.svelte"; import Codemirror from "../../libs/Codemirror.svelte";
import { getErrorMessage } from "../form.js"; import { getErrorMessage } from "./errorMessage";
export let value; export let value;
@@ -1,6 +1,6 @@
<script> <script>
import Codemirror from "../../../libs/CodemirrorMarkdown.svelte"; import Codemirror from "../../libs/CodemirrorMarkdown.svelte";
import { getErrorMessage } from "../form.js"; import { getErrorMessage } from "./errorMessage";
export let value; export let value;
@@ -1,6 +1,6 @@
<script> <script>
import Datalist from "./Datalist.svelte"; import Datalist from "./Datalist.svelte";
import { getErrorMessage } from "../form.js"; import {getErrorMessage} from "./errorMessage";
export let field; export let field;
export let value; export let value;
@@ -1,8 +1,8 @@
<script> <script>
import {getContext} from "svelte"; import {getContext} from "svelte";
import {insertEdges} from "../form/references/reference.js"; import {insertEdges} from "./reference";
import PreviewCard from "../PreviewCard.svelte"; import PreviewCard from "../PreviewCard.svelte";
import {getErrorMessage} from "../form/errorMessage.js"; 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";
@@ -14,7 +14,12 @@
export let validationErrors; export let validationErrors;
$: errorMessage = getErrorMessage(validationErrors, field.name); $: errorMessage = getErrorMessage(validationErrors, field.name);
$: currentReferences = graph._children[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 collections = channel.schemas.filter((aschema) => let collections = channel.schemas.filter((aschema) =>
field.collections.includes(aschema.name) field.collections.includes(aschema.name)
@@ -28,7 +33,8 @@
} }
function reorder(e) { function reorder(e) {
graph.edges = sortByField(e.detail.source, e.detail.target, graph.edges, field.name);
graph.edges = sortByField(e.detail.source, e.detail.target, graph.edges, field.name, references);
} }
function insert(e) { function insert(e) {
@@ -52,9 +58,9 @@
on:save={insert} on:save={insert}
/> />
</div> </div>
{#if currentReferences.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 currentReferences as reference (reference.id)} {#each references as reference (reference.id)}
<div class="col mb-3"> <div class="col mb-3">
<PreviewCard <PreviewCard
classes="h-100" classes="h-100"
@@ -2,7 +2,7 @@
import {getContext} from "svelte"; import {getContext} from "svelte";
import {uniqBy} from "lodash"; import {uniqBy} from "lodash";
import PreviewCardInline from "../PreviewCardInline.svelte"; import PreviewCardInline from "../PreviewCardInline.svelte";
import {getErrorMessage} from "../form/errorMessage.js"; 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 {flip} from "svelte/animate"; import {flip} from "svelte/animate";
@@ -119,7 +119,7 @@
} }
function move(e, from, to) { function move(e, from, to) {
graph.edges = sortByField(from, to, graph.edges, field.name); graph.edges = sortByField(from, to, graph.edges, field.name, references);
} }
</script> </script>
@@ -2,7 +2,7 @@
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 BrowseModal from "../form/references/BrowseModal.svelte"; import BrowseModal from "./BrowseModal.svelte";
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
// export let field; // export let field;
@@ -1,13 +1,13 @@
<script> <script>
import {getContext} from "svelte"; import {getContext} from "svelte";
import {previewTitle} from "../Preview"; import {previewTitle} from "../Preview";
import {getErrorMessage} from "../form/errorMessage.js"; 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 RenderField from "../../content/RenderField.svelte"; import RenderField from "../../content/RenderField.svelte";
import Icon from "../../common/Icon.svelte"; import Icon from "../../common/Icon.svelte";
import {insertEdges} from "../form/references/reference.js"; import {insertEdges} from "./reference.js";
const channel = getContext("channel"); const channel = getContext("channel");
export let field; export let field;
@@ -57,7 +57,7 @@
function reorder(e) { function reorder(e) {
graph.edges = sortByField(e.detail.source, e.detail.target, graph.edges, field.name); graph.edges = sortByField(e.detail.source, e.detail.target, graph.edges, field.name, references);
} }
@@ -1,14 +1,8 @@
<script> <script>
import {getContext} from "svelte"; import {getContext} from "svelte";
import {uniqBy, debounce} from "lodash"; import {debounce} from "lodash";
import {previewTitle} from "../../Preview.js"; import {previewTitle} from "../Preview";
import {getErrorMessage} from "../errorMessage.js"; import {getErrorMessage} from "./errorMessage";
import {sortByField} from "../../../edges/sortEdges.js";
import ReferenceInlineButtons from "../../elements/ReferenceInlineButtons.svelte";
import Sortable from "../../../libs/Sortable.svelte";
import RenderField from "../../../content/RenderField.svelte";
import Icon from "../../../common/Icon.svelte";
import Datalist from "../fields/Datalist.svelte";
import {insertEdges} from "./reference.js"; import {insertEdges} from "./reference.js";
const channel = getContext("channel"); const channel = getContext("channel");
@@ -1,6 +1,6 @@
<script> <script>
import Tinymce from "../../../libs/Tinymce.svelte"; import Tinymce from "../../libs/Tinymce.svelte";
import { getErrorMessage } from "../form.js"; import {getErrorMessage} from "./errorMessage";
export let value; export let value;
export let field; export let field;
@@ -1,5 +1,5 @@
<script> <script>
import { getErrorMessage } from "../form.js"; import { getErrorMessage } from "./errorMessage";
export let field; export let field;
export let value; export let value;
export let isCreateMode; export let isCreateMode;
@@ -1,7 +1,7 @@
<script> <script>
import Datalist from "./Datalist.svelte"; import Datalist from "./Datalist.svelte";
import Selectlist from "./Selectlist.svelte"; import Selectlist from "./Selectlist.svelte";
import { getErrorMessage } from "../form.js"; import {getErrorMessage} from "./errorMessage";
export let field; export let field;
export let value; export let value;
@@ -1,6 +1,6 @@
<script> <script>
import { onMount } from "svelte"; import { onMount } from "svelte";
import { getErrorMessage } from "../form.js"; import { getErrorMessage } from "./errorMessage";
export let field; export let field;
export let value; export let value;
export let isCreateMode; export let isCreateMode;
@@ -1,8 +1,8 @@
<script> <script>
import { v4 as uuidv4 } from "uuid"; 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 "../form.js"; import { getErrorMessage } from "./errorMessage";
const channelurl = getContext("channelurl"); const channelurl = getContext("channelurl");
export let validationErrors; export let validationErrors;
$: errorMessage = getErrorMessage(validationErrors, field.name); $: errorMessage = getErrorMessage(validationErrors, field.name);
@@ -0,0 +1,30 @@
<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();
</script>
<div class="mb-0">
<div class="d-flex justify-content-between">
<label for={id} class="form-label">{field.label}</label>
<a
class="text-decoration-none"
href="{channelurl}/schemas/{schema.name}/fields/edit/{field.name}"
><code class="text-primary opacity-50">{field.name}</code></a
>
</div>
<input
type="url"
{id}
class="form-control"
bind:value
placeholder="https://www.example.com"
/>
{#if field.help}
<small class=" text-primary opacity-50">{field.help}</small>
{/if}
</div>
@@ -0,0 +1,5 @@
export function getErrorMessage(validationErrors, fieldName) {
return validationErrors && validationErrors[fieldName]
? validationErrors[fieldName].message
: null;
}
@@ -0,0 +1,24 @@
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: ""
};
});
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;
}
-119
View File
@@ -1,119 +0,0 @@
<script>
import {afterUpdate, createEventDispatcher, onMount} from "svelte";
import {isEqual} from "../deepEquality.js";
import ContentTabs from "../ContentTabs.svelte"
import ErrorAlert from "../../common/ErrorAlert.svelte"
import FormField from "./FormField.svelte";
import ReferenceField from "./ReferenceField.svelte";
import SaveButtons from "./SaveButtons.svelte";
import EditHeader from "../EditHeader.svelte";
const dispatch = createEventDispatcher();
function save() {
dispatch("save", {
status: status
});
}
export let title = null;
export let schema;
export let record;
export let data;
export let status = null;
export let graph;
export let isCreateMode;
let originalContent;
let activeContentTab = "";
$: hasUnsavedData = false;
export let validationErrors = null;
export let errorMessage = validationErrors
? `Record submission failed. ${
Object.entries(validationErrors).length
} error(s)`
: null;
export function setOriginalData() {
originalContent = {
data: JSON.parse(JSON.stringify(data)),
status: status,
edges: JSON.parse(JSON.stringify(graph?.map(r => r.edge.target+r.edge.field) ?? [])).sort(),
};
hasUnsavedData = checkUnsavedData();
}
onMount(() => {
setOriginalData()
})
afterUpdate(() => {
hasUnsavedData = checkUnsavedData();
});
function beforeUnload(e) {
// Cancel the event as stated by the standard.
// e.preventDefault();
// console.log(hasUnsavedData);
if (hasUnsavedData) {
return (e.returnValue =
"You have unsaved changes. Are you sure you want to exit?");
}
// Chrome requires returnValue to be set.
// e.returnValue = "";
delete e["returnValue"];
// more compatibility
// return true;
return "...";
}
function checkUnsavedData() {
if (isCreateMode) {
return false;
}
return !isEqual(originalContent, {
data: data,
status: status,
edges: graph?.map(r => r.edge.target+r.edge.field).sort() ?? [],
});
}
</script>
<svelte:window on:beforeunload={beforeUnload}/>
<div>
<EditHeader {schema} {record} {isCreateMode} {title}/>
<div class=" mt-4" style="margin-bottom:150px">
<SaveButtons on:save={save} bind:status {hasUnsavedData} {isCreateMode}/>
<ErrorAlert message={errorMessage}/>
<ContentTabs
{schema}
bind:active={activeContentTab}
/>
<!-- <fieldset disabled="disabled"> -->
{#each schema.fields as field (field.name)}
{#if activeContentTab === field.group}
{#if ["reference", "file"].includes(field.info.name)}
<ReferenceField
bind:graph={graph}
{field}
{record}
/>
{:else}
<FormField
bind:data={data}
{field}
{validationErrors}
{isCreateMode}
/>
{/if}
{/if}
{/each}
<!-- </fieldset> -->
</div>
</div>
@@ -1,51 +0,0 @@
<script>
import Text from "./fields/Text.svelte";
import Slug from "./fields/Slug.svelte";
import Color from "./fields/Color.svelte";
import Checkbox from "./fields/Checkbox.svelte";
import Number from "./fields/Number.svelte";
import Date from "./fields/Date.svelte";
import UUID from "./fields/UUID.svelte";
import Textarea from "./fields/Textarea.svelte";
import Datetime from "./fields/Datetime.svelte";
import RichEditor from "./fields/RichEditor.svelte";
import Json from "./fields/JSON.svelte";
import Markdown from "./fields/Markdown.svelte";
import FieldHeader from "./FieldHeader.svelte";
const formElements = {
text: Text,
slug: Slug,
textarea: Textarea,
rich: RichEditor,
color: Color,
checkbox: Checkbox,
number: Number,
date: Date,
datetime: Datetime,
uuid: UUID,
json: Json,
markdown: Markdown,
};
export let field;
export let data;
export let validationErrors;
export let isCreateMode;
let formElement = formElements[field.info.name];
const uniqueId = `field-${field.name}-id`;
</script>
<div class="card editor-field">
<FieldHeader {field} id={uniqueId}/>
<svelte:component
this={formElement}
bind:value={data[field.name]}
{field}
{validationErrors}
{isCreateMode}
id={uniqueId}
/>
</div>
@@ -1,17 +0,0 @@
<script>
import File from "./references/Reference.svelte";
import FieldHeader from "./FieldHeader.svelte";
export let field;
export let record;
export let graph;
// export let validationErrors;
// export let isCreateMode;
const id = `field-${field.name}-${record.id}`;
</script>
<div class="card editor-field">
<FieldHeader {field} {id}/>
<File bind:graph {record} {field} />
</div>
@@ -1,47 +0,0 @@
<script>
import StatusSelect from "./StatusSelect.svelte"
import {createEventDispatcher} from "svelte";
export let status;
export let isCreateMode;
export let hasUnsavedData;
const dispatch = createEventDispatcher();
function save(){
dispatch("save");
}
</script>
<div class="record-status-bar">
<div
class="d-flex mt-3 mb-3 align-items-center justify-content-between"
>
<StatusSelect bind:status={status}/>
{#if isCreateMode}
<button
class="ms-2 btn btn-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="ms-2 btn btn-primary btn-spinner"
on:click={save}
>
<span
class="spinner-border spinner-border-sm"
role="status"
aria-hidden="true"
/>
Save
</button>
{/if}
</div>
</div>
@@ -1,64 +0,0 @@
<script>
import {getStatus, getStatusList} from "../StatusText.js";
import SwitchButton from "../../common/SwitchButton.svelte";
export let status = "draft";
let dropdown;
$: currentStatus = getStatus(status);
const statusList = Object.values(getStatusList());
function updateStatus(e, statusValue) {
// e.preventDefault();
status = statusValue;
dropdown.click();
}
function switchStatus(e){
console.log("Asf")
if(currentStatus.value === "draft"){
status = "published"
}
if(currentStatus.value === "published"){
status = "draft"
}
}
</script>
{#if status}
<!-- Example split danger button -->
<div class="form-check form-switch" >
<input on:click={switchStatus} class="form-check-input" type="checkbox" role="switch" id="record-status-switch" checked={status === "published"}>
<label class="form-check-label" for=record-status-switch>{currentStatus.text}</label>
</div>
<!-- <div class="d-flex justify-content-between">-->
<!-- <div class="btn-group dropup">-->
<!-- <button type="button" class="btn btn-{currentStatus.bg}"-->
<!-- >{currentStatus.text}</button-->
<!-- >-->
<!-- <button-->
<!-- bind:this={dropdown}-->
<!-- type="button"-->
<!-- class="btn btn-{currentStatus.bg} dropdown-toggle dropdown-toggle-split"-->
<!-- data-bs-toggle="dropdown"-->
<!-- aria-expanded="false"-->
<!-- >-->
<!-- <span class="visually-hidden">Toggle Dropdown</span>-->
<!-- </button>-->
<!-- <div class="dropdown-menu">-->
<!-- <div class="dropdown-header">Change status to</div>-->
<!-- {#each statusList as astatus}-->
<!-- {#if astatus.value !== status}-->
<!-- <button-->
<!-- type="button"-->
<!-- class="dropdown-item my-2 rounded w-100 bg-{astatus.bg} text-{astatus.color}"-->
<!-- on:click={(e) => updateStatus(e, astatus.value)}-->
<!-- >-->
<!-- {astatus.text}-->
<!-- </button>-->
<!-- {/if}-->
<!-- {/each}-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
{/if}
-3
View File
@@ -1,3 +0,0 @@
export function getErrorMessage(validationErrors, fieldName) {
return validationErrors?.find((e) => e.fieldName === fieldName)?.message;
}
@@ -1,75 +0,0 @@
<script>
import {getContext} from "svelte";
import Form from "../Form.svelte";
import OffCanvas from "../../../common/OffCanvas.svelte";
import PreviewCard from "../../PreviewCard.svelte";
import axios from "axios";
export let field;
export let record;
export let edge;
let form;
let offCanvas;
$: validationErrors = null;
$: errorMessage = null;
const channel = getContext("channel");
let schema = channel.schemas.find(s => s.name === field.data);
export function openEdit() {
offCanvas.show();
}
function save(e){
e.preventDefault();
console.log("SAVE: Attempt");
validationErrors = null;
return new Promise(function (resolve, reject) {
axios
.put(channel.lucentUrl + "/edges", edge)
.then(function (response) {
console.log("SAVE: SAVED");
edge = response.data;
form.setOriginalData();
resolve(null);
offCanvas.hide();
})
.catch(function (error) {
// setOriginalContent();
if (error.response) {
if (typeof error.response.data.error === "string") {
errorMessage = error.response.data.error;
} else {
validationErrors = error.response.data.error;
console.log(validationErrors)
}
}
resolve(null);
});
});
}
</script>
<OffCanvas bind:this={offCanvas}>
<div class="p-4">
<PreviewCard
{record}
hasDelete={false}
editable={false}
{field}
/>
</div>
<Form
bind:this={form}
data={edge.data}
title={"Relational Data for " + field.info.label}
{schema}
isCreateMode={false}
{errorMessage}
{validationErrors}
on:save={save}
/>
</OffCanvas>
@@ -1,24 +0,0 @@
import {uniqBy} from "lodash";
export function insertEdges(existingRecords, sourceRecord, targetRecords, fieldName, action = "") {
let newQueryRecords = targetRecords.map((r) => {
return {
record: r,
edge: {
target: r.id,
source: sourceRecord.id,
sourceSchema: sourceRecord.schema,
targetSchema: r.schema,
field: fieldName,
data: {},
rank: ""
}
};
});
if (action === "replace") {
existingRecords = existingRecords.filter(queryRecord => queryRecord.edge.field !== fieldName)
}
existingRecords = [...existingRecords ?? [], ...newQueryRecords];
return uniqBy(existingRecords, (r) => r.record.id);
}
+1 -10
View File
@@ -5,8 +5,7 @@
"packages": { "packages": {
"": { "": {
"dependencies": { "dependencies": {
"@codemirror/lang-markdown": "^6.2.2", "@codemirror/lang-markdown": "^6.2.2"
"fast-equals": "^5.0.1"
}, },
"devDependencies": { "devDependencies": {
"@codemirror/commands": "^6.1.2", "@codemirror/commands": "^6.1.2",
@@ -854,14 +853,6 @@
"node": ">=12" "node": ">=12"
} }
}, },
"node_modules/fast-equals": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.0.1.tgz",
"integrity": "sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/fill-range": { "node_modules/fill-range": {
"version": "7.0.1", "version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+1 -2
View File
@@ -29,7 +29,6 @@
"vite": "^3.2.3" "vite": "^3.2.3"
}, },
"dependencies": { "dependencies": {
"@codemirror/lang-markdown": "^6.2.2", "@codemirror/lang-markdown": "^6.2.2"
"fast-equals": "^5.0.1"
} }
} }
+1 -59
View File
@@ -2,73 +2,15 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
//background-color: rgba(255,255,255,1); background-color: rgba(255,255,255,1);
margin-bottom:0px ; margin-bottom:0px ;
.nav-item{ .nav-item{
padding:16px 0; padding:16px 0;
margin: 0 16px; margin: 0 16px;
color: $primary; color: $primary;
} }
a{ a{
text-decoration: none; text-decoration: none;
} }
}
.accordion {
border: none;
}
.accordion-item {
border: none;
background: transparent;
}
.accordion-button:not(.collapsed) {
box-shadow: none;
}
.offcanvas-body {
padding: 0;
overflow: hidden;
}
.accordion-button:not(.collapsed) {
background: rgba(var(--lucent-bg), 0.2) !important;
font-weight: bold;
}
.accordion .list-group-item {
margin-left: 20px;
border-left: 2px solid rgba(var(--lucent-bg-dark), 0.2);
border-top: none;
border-bottom: none;
}
.accordion .list-group-item:hover {
background: rgba(var(--lucent-bg-dark), 0.1) !important;
}
.accordion .list-group-item.active {
border-left: 2px solid rgba(var(--lucent-bg-dark), 0.8);
border-top: none;
border-bottom: none;
background: rgba(var(--lucent-bg-dark), 0.1) !important;
color: var(--bs-primary);
}
.sidebar-logo{
padding: 5px 20px;
border-bottom: 1px solid rgba(var(--lucent-bg-dark), 0.2);
}
.sidebar-logo a{
text-decoration: none;
font-size: 25px;
color: var(--bs-primary);
} }
-8
View File
@@ -1,8 +0,0 @@
.record-status-bar {
//position: fixed;
//bottom: 0;
//left: 0px;
//width: 100%;
//background: rgb(var(--lucent-bg-dark)); // old 206, 223, 210
//z-index: 1050;
}
+74 -12
View File
@@ -3,21 +3,86 @@
/* SCSS HEX */ /* SCSS HEX */
$green-crayola: #0dab76ff; $green-crayola: #0dab76ff;
$green-pigment: #139a43ff; $green-pigment: #139a43ff;
$lincoln-green: #3e5d8f; $lincoln-green: #0b5d1eff;
$forest-green-traditional: #053b06ff; $forest-green-traditional: #053b06ff;
$black: #000000ff; $black: #000000ff;
:root { /* SCSS Gradient */
/* #f0f0f0 in decimal RGB */ $gradient-top: linear-gradient(
--lucent-bg:216, 223, 233; //old 11, 93, 30 0deg,
--lucent-bg-dark:34, 44, 60; //old 11, 93, 30 #0dab76ff,
} #139a43ff,
#0b5d1eff,
#053b06ff,
#000000ff
);
$gradient-right: linear-gradient(
90deg,
#0dab76ff,
#139a43ff,
#0b5d1eff,
#053b06ff,
#000000ff
);
$gradient-bottom: linear-gradient(
180deg,
#0dab76ff,
#139a43ff,
#0b5d1eff,
#053b06ff,
#000000ff
);
$gradient-left: linear-gradient(
270deg,
#0dab76ff,
#139a43ff,
#0b5d1eff,
#053b06ff,
#000000ff
);
$gradient-top-right: linear-gradient(
45deg,
#0dab76ff,
#139a43ff,
#0b5d1eff,
#053b06ff,
#000000ff
);
$gradient-bottom-right: linear-gradient(
135deg,
#0dab76ff,
#139a43ff,
#0b5d1eff,
#053b06ff,
#000000ff
);
$gradient-top-left: linear-gradient(
225deg,
#0dab76ff,
#139a43ff,
#0b5d1eff,
#053b06ff,
#000000ff
);
$gradient-bottom-left: linear-gradient(
315deg,
#0dab76ff,
#139a43ff,
#0b5d1eff,
#053b06ff,
#000000ff
);
$gradient-radial: radial-gradient(
#0dab76ff,
#139a43ff,
#0b5d1eff,
#053b06ff,
#000000ff
);
$primary: $lincoln-green; $primary: $lincoln-green;
$table-striped-bg-factor: 0.03; $table-striped-bg-factor: 0.03;
$dropdown-bg: rgb(var(--lucent-bg)); //rgb(206, 223, 210) $dropdown-bg: rgb(206, 223, 210);
@import "../node_modules/bootstrap/scss/bootstrap"; @import "../node_modules/bootstrap/scss/bootstrap";
@import "./sidebar"; @import "./sidebar";
@@ -31,9 +96,6 @@ $dropdown-bg: rgb(var(--lucent-bg)); //rgb(206, 223, 210)
@import "./nav"; @import "./nav";
@import "./files"; @import "./files";
@import "./revisions"; @import "./revisions";
@import "./record-status-bar";
body { body {
background-color: rgba(11, 93, 30, 0.04); background-color: rgba(11, 93, 30, 0.04);
-6
View File
@@ -1,6 +0,0 @@
{
"name": "lucent-package",
"lockfileVersion": 3,
"requires": true,
"packages": {}
}
+1 -1
View File
@@ -3,7 +3,7 @@
namespace Lucent\Account; namespace Lucent\Account;
use Lucent\Channel\ChannelService; use Lucent\Channel\ChannelService;
use Lucent\Support\Collection; use Lucent\Primitive\Collection;
readonly class AccountService readonly class AccountService
{ {
+2 -2
View File
@@ -4,8 +4,8 @@ namespace Lucent\Account;
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Lucent\Support\Collection; use Lucent\Primitive\Collection;
use Lucent\Support\Option\Option; use PhpOption\Option;
class UserRepo class UserRepo
{ {
+4 -1
View File
@@ -14,7 +14,10 @@ class ArrayContainer implements ArrayAccess, JsonSerializable
{ {
} }
public function get(string $key, mixed $default = null): mixed
{
return $this->data[$key] ?? $default;
}
public function offsetSet($offset, $value): void public function offsetSet($offset, $value): void
{ {
+2 -2
View File
@@ -2,8 +2,8 @@
namespace Lucent\Channel; namespace Lucent\Channel;
use Lucent\Schema\Schema\Schema; use Lucent\Primitive\Collection;
use Lucent\Support\Collection; use Lucent\Schema\Schema;
final class Channel final class Channel
{ {
+3 -3
View File
@@ -3,10 +3,10 @@
namespace Lucent\Channel; namespace Lucent\Channel;
use Lucent\Schema\Schema\Schema; use Lucent\Primitive\Collection;
use Lucent\Schema\Schema;
use Lucent\Schema\SchemaService; use Lucent\Schema\SchemaService;
use Lucent\Support\Collection; use PhpOption\Option;
use Lucent\Support\Option\Option;
final class ChannelService final class ChannelService
{ {
@@ -1,12 +1,12 @@
<?php <?php
namespace Lucent\Schema\Commands; namespace Lucent\Commands;
use DirectoryIterator; use DirectoryIterator;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Lucent\Schema\Schema\Schema; use Lucent\Schema\Schema;
use Lucent\Schema\Schema\Type;
use Lucent\Schema\SchemaService; use Lucent\Schema\SchemaService;
use Lucent\Schema\Type;
class CompileSchemas extends Command class CompileSchemas extends Command
{ {
+2 -2
View File
@@ -7,8 +7,8 @@ use Exception;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Intervention\Image\ImageManager; use Intervention\Image\ImageManager;
use Lucent\Channel\ChannelService; use Lucent\Channel\ChannelService;
use Lucent\Schema\Schema\Schema; use Lucent\Schema\Schema;
use Lucent\Schema\Schema\Type; use Lucent\Schema\Type;
class RebuildThumbnails extends Command class RebuildThumbnails extends Command
{ {
+1 -1
View File
@@ -3,7 +3,7 @@
namespace Lucent\Commands; namespace Lucent\Commands;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Lucent\Graph\Edge\EdgeService; use Lucent\Edge\EdgeService;
use Lucent\Query\Query; use Lucent\Query\Query;
class RemoveOrphanEdges extends Command class RemoveOrphanEdges extends Command
-2
View File
@@ -3,8 +3,6 @@
return [ return [
"env" => env("LUCENT_ENV", "production"), "env" => env("LUCENT_ENV", "production"),
"schemas_path" => env("LUCENT_SCHEMAS_PATH", "app/Lucent"), "schemas_path" => env("LUCENT_SCHEMAS_PATH", "app/Lucent"),
"json_schemas_path" => env("LUCENT_JSON_SCHEMA_PATH", "json_schema"),
"sidebar_path" => env("LUCENT_SIDEBAR_PATH", "app/Lucent"),
"database" => env('LUCENT_DB_CONNECTION', env('DB_CONNECTION',"sqlite")), "database" => env('LUCENT_DB_CONNECTION', env('DB_CONNECTION',"sqlite")),
"name" => env("LUCENT_NAME", "Lucent"), "name" => env("LUCENT_NAME", "Lucent"),
"url" => env("LUCENT_URL", env('APP_URL')), "url" => env("LUCENT_URL", env('APP_URL')),
-32
View File
@@ -1,32 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('edges', function (Blueprint $table) {
$table->jsonb('data')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('records', function (Blueprint $table) {
$table->dropColumn("data");
});
}
};
@@ -1,33 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('revisions', function (Blueprint $table) {
$table->dropColumn("schema");
$table->dropColumn("_file");
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('revisions', function (Blueprint $table) {
$table->string('schema');
$table->jsonb('_file');
});
}
};
@@ -18,7 +18,7 @@ return new class extends Migration {
$table->string('status'); $table->string('status');
$table->jsonb('data'); $table->jsonb('data');
$table->jsonb('_sys'); $table->jsonb('_sys');
$table->jsonb('_file')->nullable(); $table->jsonb('_file');
$table->index(['schema', 'status']); $table->index(['schema', 'status']);
}); });
@@ -21,7 +21,6 @@ return new class extends Migration {
$table->jsonb('_file'); $table->jsonb('_file');
$table->jsonb('_edges'); $table->jsonb('_edges');
}); });
} }
/** /**
+61
View File
@@ -0,0 +1,61 @@
<?php
namespace Lucent\Edge;
use Lucent\LucentException;
use Lucent\Validator\Validator as LucentValidator;
final class Edge
{
/**
* @throws LucentException
*/
public function __construct(
public string $source,
public string $target,
public string $sourceSchema,
public string $targetSchema,
public string $field,
public string $rank = "a",
public int $depth = 0,
)
{
LucentValidator::single("source", $source, "required|uuid");
LucentValidator::single("target", $target, "required|uuid");
}
public function equal(Edge $edge): bool
{
return $this->targetSchema === $edge->targetSchema && $this->field === $edge->field && $this->target === $edge->target && $this->source === $edge->source;
}
public function toArray(): array
{
return json_decode(json_encode($this), true);
}
public function toDB(): array
{
$data = $this->toArray();
unset($data["depth"]);
return $data;
}
public static function fromArray(array $data): Edge
{
return new Edge(
source: data_get($data, 'source'),
target: data_get($data, 'target'),
sourceSchema: data_get($data, 'sourceSchema'),
targetSchema: data_get($data, 'targetSchema'),
field: data_get($data, 'field'),
rank: data_get($data, 'rank'),
depth: data_get($data, 'depth', 0),
);
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
namespace Lucent\Edge;
use Illuminate\Support\Collection;
/**
* @extends \Illuminate\Support\Collection<int|string, Edge>
*/
final class EdgeCollection extends Collection
{
public function __construct(
Edge ...$array
) {
parent::__construct($array);
}
/**
* @return Edge[]
**/
public function toArray(): array
{
return collect($this)->values()->toArray();
}
public static function fromArray(array $data): EdgeCollection
{
$edges = array_map([Edge::class, 'fromArray'], $data);
return new EdgeCollection(...$edges);
}
}
+68
View File
@@ -0,0 +1,68 @@
<?php namespace Lucent\Edge;
use Illuminate\Support\Facades\DB;
use Lucent\LucentException;
use PDOException;
use stdClass;
class EdgeRepo
{
public function __construct()
{
}
public function insert(Edge $edge): void
{
try {
DB::table("edges")->insert($edge->toDB());
} catch (PDOException $e) {
if ($e->getCode() == 23505) {
throw new LucentException("Edge already exists");
}
throw $e;
}
}
public function update(string $from, EdgeCollection $edges): void
{
$edgesDB = collect($edges)->map(fn($e) => $e->toDB())->toArray();
DB::table("edges")->where("source", $from)->delete();
DB::table("edges")->insert($edgesDB);
}
public function findAll(): EdgeCollection
{
$edges = DB::table("edges")->get();
return new EdgeCollection(...$edges->map([$this, 'mapEdge'])->toArray());
}
public function mapEdge(stdClass $edge): Edge
{
return new Edge(
source: $edge->source,
target: $edge->target,
sourceSchema: $edge->sourceSchema,
targetSchema: $edge->targetSchema,
field: $edge->field,
rank: $edge->rank,
depth: $edge->depth ?? 0
);
}
public function remove(Edge $edge): void
{
DB::table("edges")
->where("source", $edge->source)
->where("target", $edge->target)
->where("sourceSchema", $edge->sourceSchema)
->where("targetSchema", $edge->targetSchema)
->where("field", $edge->field)
->delete();
}
}
+53
View File
@@ -0,0 +1,53 @@
<?php namespace Lucent\Edge;
use Lucent\LucentException;
class EdgeService
{
public function __construct(public EdgeRepo $edgeRepo)
{
}
/**
* @throws LucentException
*/
public function create(
string $source,
string $target,
string $sourceSchema,
string $targetSchema,
string $field,
string $rank,
): Edge
{
$edge = new Edge(
source: $source,
target: $target,
sourceSchema: $sourceSchema,
targetSchema: $targetSchema,
field: $field,
rank: $rank,
);
$this->edgeRepo->insert($edge);
return $edge;
}
public function update(string $from, EdgeCollection $edges): void
{
$this->edgeRepo->update($from, $edges);
}
public function findAll(): EdgeCollection
{
return $this->edgeRepo->findAll();
}
public function remove(Edge $edge): void
{
$this->edgeRepo->remove($edge);
}
}
+5 -15
View File
@@ -4,12 +4,11 @@ namespace Lucent\File;
use Illuminate\Http\UploadedFile; use Illuminate\Http\UploadedFile;
use Lucent\Channel\ChannelService; use Lucent\Channel\ChannelService;
use Lucent\Graph\Record\FileInfo;
use Lucent\Graph\Record\QueryRecord;
use Lucent\LucentException; use Lucent\LucentException;
use Lucent\Schema\Schema\Schema; use Lucent\Record\File;
use Lucent\Schema\Schema\Type; use Lucent\Record\QueryRecord;
use Lucent\Support\Result\Result; use Lucent\Schema\Schema;
use Lucent\Schema\Type;
class FileService class FileService
{ {
@@ -25,15 +24,6 @@ class FileService
return $this->channelService->channel->url. "/storage/".$file->_file->path; return $this->channelService->channel->url. "/storage/".$file->_file->path;
} }
/**
* @param Schema $schema
* @param string $uploadFromUrl
* @return Result<FileInfo|string>
*/
public function createFromUrl(Schema $schema, string $uploadFromUrl): Result{
}
/** /**
* @throws LucentException * @throws LucentException
*/ */
@@ -58,7 +48,7 @@ class FileService
} }
return new FileUploadResult( return new FileUploadResult(
recordFile: FileInfo::fromArray($file), duplicateId: "", isDuplicate: false recordFile: File::fromArray($file), duplicateId: "", isDuplicate: false
); );
} }
+2 -2
View File
@@ -2,13 +2,13 @@
namespace Lucent\File; namespace Lucent\File;
use Lucent\Graph\Record\FileInfo; use Lucent\Record\File;
class FileUploadResult class FileUploadResult
{ {
public function __construct( public function __construct(
public ?FileInfo $recordFile, public ?File $recordFile,
public string $duplicateId, public string $duplicateId,
public bool $isDuplicate, public bool $isDuplicate,
) )
+1 -1
View File
@@ -6,7 +6,7 @@ use Exception;
use Illuminate\Log\Logger; use Illuminate\Log\Logger;
use Intervention\Image\ImageManager; use Intervention\Image\ImageManager;
use Lucent\Channel\ChannelService; use Lucent\Channel\ChannelService;
use Lucent\Graph\Record\QueryRecord; use Lucent\Record\QueryRecord;
class ImageService class ImageService
{ {

Some files were not shown because too many files have changed in this diff Show More