diff --git a/front/js/svelte/content/Grid.svelte b/front/js/svelte/content/Grid.svelte deleted file mode 100644 index 0a68759..0000000 --- a/front/js/svelte/content/Grid.svelte +++ /dev/null @@ -1,68 +0,0 @@ - -
- {#each records as record (record.id)} -
-
- {#if isWritable} -
- select(record)} - class="form-check-input " - type="checkbox" - checked={selected.find( - (r) => r.id === record.id - )} - value={record} - /> -
- {/if} -
- -
- - {record._file.path} - {record._file.mime} -
-
- {/each} -
- - - diff --git a/front/js/svelte/content/Index.svelte b/front/js/svelte/content/Index.svelte index bed46d8..1b2a639 100644 --- a/front/js/svelte/content/Index.svelte +++ b/front/js/svelte/content/Index.svelte @@ -3,7 +3,7 @@ import Pagination from "./pagination/Pagination.svelte"; import ActionsOnSelected from "./ActionsOnSelected.svelte"; import Table from "./Table.svelte"; - import {getContext} from "svelte"; + import { getContext } from "svelte"; const axios = getContext("axios"); export let schema; @@ -47,51 +47,49 @@
-
-

+
+

{schema.label}

{#if selected.length > 0 && !inModal && isWritable} - + {:else} {/if} - - diff --git a/front/js/svelte/content/RecordRow.svelte b/front/js/svelte/content/RecordRow.svelte index 64dc522..db84cd6 100644 --- a/front/js/svelte/content/RecordRow.svelte +++ b/front/js/svelte/content/RecordRow.svelte @@ -2,8 +2,8 @@ import RenderField from "./RenderField.svelte"; import Avatar from "../account/Avatar.svelte"; import Status from "../records/Status.svelte"; - import {usernameById} from "../account/users"; - import {friendlyDate} from "../../helpers"; + import { usernameById } from "../account/users"; + import { friendlyDate } from "../../helpers"; export let schema; export let users; @@ -12,7 +12,6 @@ export let sortParam; export let sortField; export let visibleColumns; - {#each visibleColumns as field, index} @@ -20,7 +19,7 @@ class="field-ui-{field.info.name}" class:is-sort={field.name === sortField.name} > - + {/each} {#if schema.visible?.includes("status")} @@ -28,32 +27,40 @@ class="text-center" class:is-sort={"-status" == sortParam || "status" == sortParam} > - + {/if} {#if schema.visible?.includes("_sys.createdBy")} {/if} {#if schema.visible?.includes("_sys.updatedBy")} {/if} {#if schema.visible?.includes("_sys.createdAt")} - {/if} {#if schema.visible?.includes("_sys.updatedAt")} - {/if} diff --git a/front/js/svelte/content/Table.svelte b/front/js/svelte/content/Table.svelte index e863cca..675d48a 100644 --- a/front/js/svelte/content/Table.svelte +++ b/front/js/svelte/content/Table.svelte @@ -1,13 +1,13 @@ -
+
- + - + - {friendlyDate(record._sys.createdAt)} + + {friendlyDate(record.createdAt)} - {friendlyDate(record._sys.updatedAt)} + + {friendlyDate(record.updatedAt)}
- - {#if isWritable} - - {/if} + > + + {/if} - {#each visibleColumns as field} - - {/each} - {#each systemFields.filter(c => schema.visible?.includes(c.name)) as sysField} - - {/each} - - + title={field.help}>{field.label} + {/each} + {#each systemFields.filter( (c) => schema.visible?.includes(c.name), ) as sysField} + + {/each} + + - {#each records as record (record.id)} - - - + + - - - {/each} + + + {/each}
- + {#if isWritable} + + 0 && selected.length < records.length} + indeterminate={selected.length > 0 && + selected.length < records.length} checked={selected.length === records.length} - > - - {field.label}{sysField.label}
{sysField.label}
-
- {#if isWritable} - +
+
+ {#if isWritable} + select(record)} - checked={selected.find((r) => r.id === record.id)} + checked={selected.find( + (r) => r.id === record.id, + )} value={record} - > - + > + {/if} - {/if} - {#if record._file?.path} -
- 0 ? "medium" : "small"}/> - -
- {#if record.status === "draft"} - {record.status} - {/if} - - {previewTitle(channel.schemas, record, graph)} - - {(record._file.size / 1024).toFixed(1)}kB - - {#if record._file.width > 0} - {record._file.width + "x" + record._file.height} - {/if} - - Download - -
- -
- {:else} {#if record.status === "draft"} - {record.status} + {record.status} {/if} {previewTitle(channel.schemas, record, graph)} - {/if} - - -
-
- -
+ +
- diff --git a/front/js/svelte/content/tools/Tools.svelte b/front/js/svelte/content/tools/Tools.svelte index fdd9fbf..f33f916 100644 --- a/front/js/svelte/content/tools/Tools.svelte +++ b/front/js/svelte/content/tools/Tools.svelte @@ -1,6 +1,5 @@
- {#if record.status === "draft"} - DRAFT - {/if} - {#if schema.type === "files"} - - {:else} - - {previewTitle(channel.schemas, record, graph)} - - {/if} + {#if record.status === "draft"} + DRAFT + {/if} + {#if schema.type === "files"} + + {:else} + + {previewTitle(channel.schemas, record, graph)} + + {/if}
-{schema.label} - - +{schema.label}
- +
{frieldlyUpdatedAt}
diff --git a/front/js/svelte/libs/CodemirrorMarkdown.svelte b/front/js/svelte/libs/CodemirrorMarkdown.svelte index 703fbee..cb7d10c 100644 --- a/front/js/svelte/libs/CodemirrorMarkdown.svelte +++ b/front/js/svelte/libs/CodemirrorMarkdown.svelte @@ -1,14 +1,13 @@ -
+
diff --git a/front/js/svelte/libs/Trix.svelte b/front/js/svelte/libs/Trix.svelte index 566b550..df908ee 100644 --- a/front/js/svelte/libs/Trix.svelte +++ b/front/js/svelte/libs/Trix.svelte @@ -1,68 +1,66 @@
- +
diff --git a/front/js/svelte/records/Edit.svelte b/front/js/svelte/records/Edit.svelte index 64b63d7..71edd4f 100644 --- a/front/js/svelte/records/Edit.svelte +++ b/front/js/svelte/records/Edit.svelte @@ -1,14 +1,13 @@ - +
- + {#if isCreateMode} - {:else if hasUnsavedData} {/if} -
- + <Title {schema} {record} {isCreateMode} /> - - <ErrorAlert message={errorMessage}/> + <ErrorAlert message={errorMessage} /> <div class=" mt-4" style="margin-bottom:150px;position:relative;"> - <ContentTabs - {schema} - {isCreateMode} - bind:active={activeContentTab} - /> + <ContentTabs {schema} {isCreateMode} bind:active={activeContentTab} /> {#if !["_graph", "_info"].includes(activeContentTab)} - <FilePreview {record} {schema}/> {#each activeFields as field (field.name)} {#if activeContentTab === field.group} <FormField - bind:data={record.data} - bind:graph={graph} - {field} - {schema} - {record} - {validationErrors} - {isCreateMode} + bind:data={record.data} + bind:graph + {field} + {schema} + {record} + {validationErrors} + {isCreateMode} /> {/if} {/each} {:else if activeContentTab === "_graph"} - <Graph {graph} {record}/> + <Graph {graph} {record} /> {:else if activeContentTab === "_info"} - <Info {record} {graph} {users} {schema}/> + <Info {record} {graph} {users} {schema} /> {/if} </div> </div> diff --git a/front/js/svelte/records/FormField.svelte b/front/js/svelte/records/FormField.svelte index cb0c29b..e3a263b 100644 --- a/front/js/svelte/records/FormField.svelte +++ b/front/js/svelte/records/FormField.svelte @@ -45,25 +45,20 @@ </script> <div class="editor-field"> - <FieldHeader {field} {id}/> + <FieldHeader {field} {id} /> {#if field.info.name === "reference" && field.layout === "tags"} - <ReferenceTags - bind:graph - {id} - {record} - {field} - {validationErrors} - /> + <ReferenceTags bind:graph {id} {record} {field} {validationErrors} /> {:else if field.info.name === "reference"} - <Reference - bind:graph - {id} + <Reference bind:graph {id} {record} {field} {validationErrors} /> + {:else if field.info.name === "file"} + <!-- <File bind:graph {record} {field} {validationErrors} /> --> + <File + bind:value={data[field.name]} {record} + {id} {field} {validationErrors} /> - {:else if field.info.name === "file"} - <File bind:graph {record} {field} {validationErrors}/> {:else if field.info.name === "text"} <Text bind:value={data[field.name]} @@ -74,11 +69,11 @@ /> {:else if field.info.name === "slug"} <Slug - bind:value={data[field.name]} - {field} - {id} - {validationErrors} - {isCreateMode} + bind:value={data[field.name]} + {field} + {id} + {validationErrors} + {isCreateMode} /> {:else if field.info.name === "textarea"} <Textarea @@ -90,23 +85,23 @@ /> {:else if field.info.name === "rich"} <RichEditor - bind:value={data[field.name]} - {schema} - {field} - {validationErrors} - {isCreateMode} - bind:graph - {record} + bind:value={data[field.name]} + {schema} + {field} + {validationErrors} + {isCreateMode} + bind:graph + {record} /> {:else if field.info.name === "markdown"} <Markdown - bind:value={data[field.name]} - {schema} - {field} - {validationErrors} - {isCreateMode} - bind:graph - {record} + bind:value={data[field.name]} + {schema} + {field} + {validationErrors} + {isCreateMode} + bind:graph + {record} /> {:else} <svelte:component diff --git a/front/js/svelte/records/Info.svelte b/front/js/svelte/records/Info.svelte index c9097dd..bcf27d3 100644 --- a/front/js/svelte/records/Info.svelte +++ b/front/js/svelte/records/Info.svelte @@ -1,11 +1,11 @@ <script> - import {friendlyDate} from "../../helpers"; + import { friendlyDate } from "../../helpers"; import Avatar from "../account/Avatar.svelte"; - import {usernameById} from "../account/users"; - import {isEqual} from "lodash"; + import { usernameById } from "../account/users"; + import { isEqual } from "lodash"; import Icon from "../common/Icon.svelte"; import RevisionCell from "./revisions/RevisionCell.svelte"; - import {getContext} from "svelte"; + import { getContext } from "svelte"; import RevisionEdgeRow from "./revisions/RevisionEdgeRow.svelte"; const channel = getContext("channel"); @@ -30,27 +30,27 @@ }); function getEdgesByField(fieldsWithDiff, revision) { - - edgeFieldsDiff = graph.edges.filter((e) => e.depth === 1).reduce((c, e) => { - if (!c[e.field]) { - c[e.field] = { - record: [], - revision: [], + edgeFieldsDiff = graph.edges + .filter((e) => e.depth === 1) + .reduce((c, e) => { + if (!c[e.field]) { + c[e.field] = { + record: [], + revision: [], + }; } - } - c[e.field]["record"].push(e) - return c; - }, {}); - + c[e.field]["record"].push(e); + return c; + }, {}); edgeFieldsDiff = revision._edges.reduce((c, e) => { if (!c[e.field]) { c[e.field] = { record: [], revision: [], - } + }; } - c[e.field]["revision"].push(e) + c[e.field]["revision"].push(e); return c; }, edgeFieldsDiff); } @@ -62,7 +62,7 @@ fieldsWithDiff = schema.fields.filter((f) => { return !isEqual(selectedRevision.data[f.name], record.data[f.name]); }); - getEdgesByField(fieldsWithDiff, revision) + getEdgesByField(fieldsWithDiff, revision); revisionSection.scrollIntoView(); } @@ -71,7 +71,7 @@ rollbackError = ""; axios .post( - `${channel.lucentUrl}/records/${record.id}/rollback/${selectedRevision._sys.version}` + `${channel.lucentUrl}/records/${record.id}/rollback/${selectedRevision.version}`, ) .then((response) => { window.location.reload(); @@ -84,7 +84,7 @@ } </script> -<div class="lx-card "> +<div class="lx-card"> <div class="row"> <div class="col-8"> <div> @@ -93,29 +93,27 @@ </div> <div> <span class="label text-end text-muted">current version </span> - {record._sys.version} + {record.version} </div> <div> <span class="label text-end text-muted"> created </span> <Avatar - name={usernameById(users, record._sys.createdBy)} - side={24} + name={usernameById(users, record.createdBy)} + side={24} /> - {friendlyDate(record._sys.createdAt)} + {friendlyDate(record.createdAt)} </div> <div> - <span class="label text-end text-muted">updated </span> + <span class="label text-end text-muted">updated </span> <Avatar - name={usernameById(users, record._sys.updatedBy)} - side={24} + name={usernameById(users, record.updatedBy)} + side={24} /> - {friendlyDate(record._sys.updatedAt)} + {friendlyDate(record.updatedAt)} </div> </div> <div class="col-4"> - <span class="label d-block text-muted " - >Rules for this schema - </span> + <span class="label d-block text-muted">Rules for this schema </span> <small> Each record maintains the last {schema.revisions} versions @@ -125,33 +123,31 @@ </div> <div class="revisions"> {#if schema.revisions > 0} - <div class="header-small mb-3">Revisions</div> + <div class="header-small mb-3">Revisions</div> {#each revisions as revision} - {#if revision._sys.version !== record._sys.version} + {#if revision.version !== record.version} <div - class="revision" - class:active={revision._sys.version === - selectedRevision?._sys.version} + class="revision" + class:active={revision.version === + selectedRevision?.version} > - <div class="version"> - <span>version {revision._sys.version}</span> + <span>version {revision.version}</span> <Avatar - name={usernameById(users, revision._sys.updatedBy)} - side={24} + name={usernameById(users, revision.updatedBy)} + side={24} /> - {friendlyDate(revision._sys.updatedAt)} + {friendlyDate(revision.updatedAt)} </div> <div class="col-3 text-center"> <button - disabled={revision._sys.version === - selectedRevision?._sys.version} - class="button" - on:click={(e) => compare(e, revision)} - >Compare - </button - > + disabled={revision.version === + selectedRevision?.version} + class="button" + on:click={(e) => compare(e, revision)} + >Compare + </button> </div> </div> {/if} @@ -169,15 +165,13 @@ <p class="text-center fw-bold mb-3 mt-5"> If you choose to rollback to this revision </p> - <button - on:click={rollback} - class="button" - > - Rollback to version {selectedRevision._sys.version} + <button on:click={rollback} class="button"> + Rollback to version {selectedRevision.version} </button> {#if rollbackError} - <span class="d-block text-danger mt-3">{rollbackError}</span> + <span class="d-block text-danger mt-3">{rollbackError}</span + > {/if} <div class="mt-3"> {#each fieldsWithDiff as field} @@ -188,31 +182,28 @@ <!-- <div class="d-block" style="width:200px;"> {field.label} </div> --> - <div - class="revision-field" - style="overflow:hidden" - > + <div class="revision-field" style="overflow:hidden"> <div class="compare-left"> <RevisionCell - {field} - side={record.data[field.name]} - colorClass="text-danger" + {field} + side={record.data[field.name]} + colorClass="text-danger" /> </div> <div class="compare-center"> <span class="me-1">{field.label}</span> <Icon - icon="angle-right" - width="12" - height="12" + icon="angle-right" + width="12" + height="12" /> </div> <div class="compare-right"> <RevisionCell - edges={selectedRevision._edges} - {field} - side={selectedRevision.data[field.name]} - colorClass="text-success" + edges={selectedRevision._edges} + {field} + side={selectedRevision.data[field.name]} + colorClass="text-success" /> </div> </div> @@ -226,22 +217,16 @@ {/if} <div class="mt-3"> - <p class="text-center fw-bold mb-3 mt-5"> - Record References - </p> + <p class="text-center fw-bold mb-3 mt-5">Record References</p> {#each Object.entries(edgeFieldsDiff) as [field, edges]} - <div - class="revision-references" - style="overflow:hidden" - > + <div class="revision-references" style="overflow:hidden"> <div class="reference-field"> {field}: </div> <div class="reference-compare"> - <p class="">Record</p> {#each edges.record as edge} - <RevisionEdgeRow {edge}/> + <RevisionEdgeRow {edge} /> {:else} <p>No references</p> {/each} @@ -249,7 +234,7 @@ <div class="reference-compare"> <p class="text-success">Revision</p> {#each edges.revision as edge} - <RevisionEdgeRow {edge}/> + <RevisionEdgeRow {edge} /> {:else} <p>No references</p> {/each} @@ -258,7 +243,5 @@ {/each} </div> </div> - {/if} </div> - diff --git a/front/js/svelte/records/InlineEdit.svelte b/front/js/svelte/records/InlineEdit.svelte index f84feb2..077219a 100644 --- a/front/js/svelte/records/InlineEdit.svelte +++ b/front/js/svelte/records/InlineEdit.svelte @@ -1,7 +1,12 @@ <script> - import {afterUpdate, createEventDispatcher, getContext, onMount} from "svelte"; + import { + afterUpdate, + createEventDispatcher, + getContext, + onMount, + } from "svelte"; - import {isEqual} from "lodash"; + import { isEqual } from "lodash"; import FormField from "./FormField.svelte"; import FilePreview from "./FilePreview.svelte"; import ContentTabs from "./header/ContentTabs.svelte"; @@ -16,7 +21,7 @@ export let record; export let graph = { records: [], - edges: [] + edges: [], }; export let isCreateMode; let originalContent; @@ -25,13 +30,11 @@ $: validationErrors = null; $: errorMessage = validationErrors ? `Record submission failed. ${ - Object.entries(validationErrors).length - } error(s)` + Object.entries(validationErrors).length + } error(s)` : null; - let activeFields = schema.fields.filter( - (f) => f.name !== "id" - ); + let activeFields = schema.fields.filter((f) => f.name !== "id"); let tabname = "_default"; let fieldToTabs = schema.fields.reduce((c, f) => { @@ -53,8 +56,6 @@ 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)), }; } @@ -87,8 +88,6 @@ data: record.data, schema: record.schema, status: record.status, - _sys: record._sys, - _file: record._file, edges: graph.edges, }); } @@ -114,8 +113,10 @@ return; } // remove trashed edges - graph.edges = graph.edges?.filter((edge) => !edge._isTrashed && edge.source === record.id) ?? []; - + graph.edges = + graph.edges?.filter( + (edge) => !edge._isTrashed && edge.source === record.id, + ) ?? []; axios .post(channel.lucentUrl + "/records", { @@ -150,64 +151,55 @@ } </script> -<svelte:window on:beforeunload={beforeUnload}/> +<svelte:window on:beforeunload={beforeUnload} /> <div class="inline-edit record-edit"> <div class="tools-header"> - <EditHeader {schema} bind:record {isCreateMode} bind:activeContentTab/> + <EditHeader {schema} bind:record {isCreateMode} bind:activeContentTab /> {#if isCreateMode} - <button - class="button primary btn-spinner" - on:click={save} - > - <span - class="spinner-border spinner-border-sm" - role="status" - aria-hidden="true" - /> + <button class="button primary btn-spinner" on:click={save}> + <span + class="spinner-border spinner-border-sm" + role="status" + aria-hidden="true" + /> Create </button> {:else if hasUnsavedData} <button - type="button" - class="button primary ms-2 btn btn-primary btn-spinner" - on:click={save} + type="button" + class="button primary ms-2 btn btn-primary btn-spinner" + on:click={save} > - <span - class="spinner-border spinner-border-sm" - role="status" - aria-hidden="true" - /> + <span + class="spinner-border spinner-border-sm" + role="status" + aria-hidden="true" + /> Save </button> {/if} - </div> - <Title {schema} {record} {isCreateMode}/> - <ErrorAlert message={errorMessage}/> + <Title {schema} {record} {isCreateMode} /> + <ErrorAlert message={errorMessage} /> <div class=" mt-4" style="margin-bottom:150px;position:relative;"> - <ContentTabs - {schema} - {isCreateMode} - bind:active={activeContentTab} - /> - <FilePreview {record} {schema}/> + <ContentTabs {schema} {isCreateMode} bind:active={activeContentTab} /> + <FilePreview {record} {schema} /> <!-- <fieldset disabled="disabled"> --> {#each activeFields as field (field.name)} {#if activeContentTab === field.group} <FormField - bind:data={record.data} - bind:graph={graph} - {field} - {schema} - {record} - {validationErrors} - {isCreateMode} + bind:data={record.data} + bind:graph + {field} + {schema} + {record} + {validationErrors} + {isCreateMode} /> {/if} {/each} <!-- </fieldset> --> </div> </div> - diff --git a/front/js/svelte/records/Preview.js b/front/js/svelte/records/Preview.js index bcc32b0..34237e6 100644 --- a/front/js/svelte/records/Preview.js +++ b/front/js/svelte/records/Preview.js @@ -1,33 +1,33 @@ import Mustache from "mustache"; -import {stripHtml} from "../../helpers"; +import { stripHtml } from "../../helpers"; export function previewTitle(schemas, record, graph) { - let schema = schemas.find((aSchema) => aSchema.name === record?.schema); - if (!schema?.cardTitle) { - return noTemplate(schema, record); - } + let schema = schemas.find((aSchema) => aSchema.name === record?.schema); + if (!schema?.cardTitle) { + return noTemplate(schema, record); + } - let recordData = record.data; - let render = Mustache.render(schema.cardTitle, recordData); - if (!render || render === "") { - return noTemplate(schema, record); - } + let recordData = record.data; + let render = Mustache.render(schema.cardTitle, recordData); + if (!render || render === "") { + return noTemplate(schema, record); + } - return stripHtml(render.slice(0, 300)); + return stripHtml(render.slice(0, 300)); } function noTemplate(schema, record) { - if (schema?.type === "files") { - return record._file.path; - } + if (schema?.type === "files") { + return file.path; + } - let title = stripHtml( - record?.data[schema.fields.filter((f) => f.info.name === "text")[0]?.name] - ).slice(0, 300); + let title = stripHtml( + record?.data[schema.fields.filter((f) => f.info.name === "text")[0]?.name], + ).slice(0, 300); - if(title.trim() === ""){ - return "~Untitled~"; - } + if (title.trim() === "") { + return "~Untitled~"; + } - return title; + return title; } diff --git a/front/js/svelte/records/elements/File.svelte b/front/js/svelte/records/elements/File.svelte index eb50b00..02a3c7f 100644 --- a/front/js/svelte/records/elements/File.svelte +++ b/front/js/svelte/records/elements/File.svelte @@ -1,10 +1,7 @@ <script> - import { sortByField } from "../../edges/sortEdges"; + import { array_move } from "../../edges/sortEdges"; import Sortable from "../../libs/Sortable.svelte"; import PreviewFile from "../previews/PreviewFile.svelte"; - import Dropdown from "../../common/Dropdown.svelte"; - import Dialog from "../../dialog/Dialog.svelte"; - import { insertEdges } from "./reference.js"; import { getContext } from "svelte"; import FileDialog from "../../dialog/FileDialog.svelte"; import Uploader from "../../files/Uploader.svelte"; @@ -12,36 +9,28 @@ const channel = getContext("channel"); export let field; export let record; - export let graph; + export let value = []; let browseModal; - function removeReference(e) { + function removeFile(e) { e.preventDefault(); - graph.edges = graph.edges.filter( - (edge) => !(edge.target === e.detail && edge.field === field.name), - ); + value = value.filter((f) => !(f.id === e.detail)); } async function reorder(e) { - graph.edges = await sortByField( - e.detail.source, - e.detail.target, - graph.edges, - field.name, - references, - ); + value = await array_move(value, e.detail.source, e.detail.target); } - function insert(e) { + function insertFiles(e) { e.preventDefault(); browseModal.close(); - graph = insertEdges( - graph, - record, - e.detail.records, - field.name, - e.detail.action, - ); + value = [...(value ?? []), ...(e.detail ?? [])]; + } + + function replaceFiles(e) { + e.preventDefault(); + browseModal.close(); + value = e.detail ?? []; } function uploadComplete(e) { @@ -61,18 +50,22 @@ <Uploader recordId={record.id} on:uploadComplete={uploadComplete} /> </div> </div> -<!-- {#if references.length > 0} +{#if value.length > 0} <Sortable sortableClass="mt-3" on:update={reorder}> - {#each references as reference (reference.id)} + {#each value ?? [] as aFile (aFile.id)} <!--This div helps the sorting thing--> -<!-- <div> + <div> <PreviewFile - record={reference} + file={aFile} hasDelete={true} - on:remove={removeReference} + on:remove_file={removeFile} ></PreviewFile> </div> {/each} </Sortable> -{/if} --> -<FileDialog bind:this={browseModal} on:insert={insert}></FileDialog> +{/if} +<FileDialog + bind:this={browseModal} + on:insert_files={insertFiles} + on:replace_files={replaceFiles} +></FileDialog> diff --git a/front/js/svelte/records/previews/PreviewFile.svelte b/front/js/svelte/records/previews/PreviewFile.svelte index 2272493..5bfe1cb 100644 --- a/front/js/svelte/records/previews/PreviewFile.svelte +++ b/front/js/svelte/records/previews/PreviewFile.svelte @@ -1,91 +1,80 @@ <script> import Icon from "../../common/Icon.svelte"; - import {createEventDispatcher, getContext} from "svelte"; + import { createEventDispatcher, getContext } from "svelte"; import Preview from "../../files/Preview.svelte"; - import {previewTitle} from "./../Preview"; - import {fileurl, htmlurl} from "../../files/imageserver.js" + import { previewTitle } from "./../Preview"; + import { fileurl, htmlurl } from "../../files/imageserver.js"; import Status from "./../Status.svelte"; import Dropdown from "../../common/Dropdown.svelte"; const dispatch = createEventDispatcher(); const channel = getContext("channel"); - export let record; + export let file; export let hasDelete = false; export let hasInsert = false; - let schema = channel.schemas.find((aschema) => aschema.name === record.schema); - let cardTitle = previewTitle(channel.schemas, record); let imagePresets = Object.keys(channel.imageFilters); function remove(e) { e.preventDefault(); - dispatch("remove", record.id); + dispatch("remove_file", file.id); } function insert(e, preset) { e.preventDefault(); - let html = htmlurl(channel, record, preset) - let url = !preset ? `/${record._file.path}` : `/templates/${preset}/${record._file.path}`; - dispatch("editor-insert", { - html: html, - url: channel.filesUrl + url, - originalUrl: channel.filesUrl + "/" + record._file.path, - record: record - }); + // let html = htmlurl(channel, record, preset); + // let url = !preset + // ? `/${record._file.path}` + // : `/templates/${preset}/${record._file.path}`; + // dispatch("editor-insert", { + // html: html, + // url: channel.filesUrl + url, + // originalUrl: channel.filesUrl + "/" + record._file.path, + // record: record, + // }); } - </script> <div class="preview-file"> <div style="display: flex;align-items: center;gap: 10px;"> <div class="image"> - <Preview {record} size="small"/> + <Preview {file} size="small" /> </div> <div class="title"> <div> - <a - class="record-title" - href="{channel.lucentUrl}/records/{record.id}" - > - {cardTitle} - </a> - <small class="d-block"> - from {schema.label} - {#if record.status === "draft"} - <Status status={record.status}/> - {/if} - </small> - + {file.filename} </div> - </div> </div> - <div style="display: flex;gap:4px; align-items: center; margin-right: 10px;"> + <div + style="display: flex;gap:4px; align-items: center; margin-right: 10px;" + > {#if hasInsert} <div class="reference-action"> <Dropdown> <div slot="button"> - <Icon icon="photo-film"/> + <Icon icon="photo-film" /> </div> - <button class="dropdown-item button" on:click={e => insert(e,null)}>original</button> + <button + class="dropdown-item button" + on:click={(e) => insert(e, null)}>original</button + > {#each imagePresets as preset} - <button class="dropdown-item button" on:click={e => insert(e,preset)}>{preset}</button> + <button + class="dropdown-item button" + on:click={(e) => insert(e, preset)}>{preset}</button + > {/each} </Dropdown> - </div> {/if} {#if hasDelete} <div class="reference-action"> - <button - class="button" - on:click={remove} - > - <Icon icon="trash-can"/> + <button class="button" on:click={remove}> + <Icon icon="trash-can" /> </button> </div> {/if} </div> </div> - diff --git a/front/js/svelte/records/revisions/RevisionCell.svelte b/front/js/svelte/records/revisions/RevisionCell.svelte index 9f7c86b..a256730 100644 --- a/front/js/svelte/records/revisions/RevisionCell.svelte +++ b/front/js/svelte/records/revisions/RevisionCell.svelte @@ -12,22 +12,9 @@ <div class="{colorClass} field-content"> <div class="d-flex align-items-center text-center flex-wrap"> {#each edges[field.name] as edgeRecord} - {#if edgeRecord._file?.path} - <div - class="ms-2 " - style="max-width:64px;overflow:hidden;white-space: nowrap;text-overflow: ellipsis;" - > - <Preview - record={edgeRecord} - size="small" - showFilename={true} - /> - </div> - {:else} - <div class="ms-2 "> - <PreviewCardSmall record={edgeRecord}/> - </div> - {/if} + <div class="ms-2"> + <PreviewCardSmall record={edgeRecord} /> + </div> {/each} </div> </div> @@ -43,7 +30,6 @@ <!-- {/if} --> <style> - .field-content { max-height: 200px; overflow-y: scroll; diff --git a/src/Channel/Channel.php b/src/Channel/Channel.php index bf4ed17..a204476 100644 --- a/src/Channel/Channel.php +++ b/src/Channel/Channel.php @@ -4,7 +4,6 @@ namespace Lucent\Channel; use Lucent\Channel\Data\UserCommand; use Lucent\Primitive\Collection; -use Lucent\Schema\FilesSchema; use Lucent\Schema\Schema; final class Channel diff --git a/src/Commands/GenerateFileSchema.php b/src/Commands/GenerateFileSchema.php deleted file mode 100644 index 56673af..0000000 --- a/src/Commands/GenerateFileSchema.php +++ /dev/null @@ -1,43 +0,0 @@ -<?php - -namespace Lucent\Commands; - -use Illuminate\Console\Command; -use Lucent\Primitive\Collection; -use Lucent\Schema\FilesSchema; - -class GenerateFileSchema extends Command -{ - - protected $signature = 'lucent:generate:file {name}'; - - protected $description = 'Generate a lucent file schema'; - - - public function handle() - { - $name = $this->argument('name'); - $schema = new FilesSchema( - name: $name, - label: $name, - fields: Collection::make(), - disk: "lucent", - path: $name, - groups: [] - ); - - $json = json_encode($schema, JSON_PRETTY_PRINT); - $configDir = base_path(config('lucent.schemas_path')); - $schemaPath = $configDir . "/" . $name . '.json'; - - if (file_exists($schemaPath)) { - $this->error("The schema file already exists."); - return 0; - } - - file_put_contents($schemaPath, $json); - $this->info("The schema file has been created."); - - } - -} diff --git a/src/Commands/RebuildThumbnails.php b/src/Commands/RebuildThumbnails.php index d21d9f2..8244705 100644 --- a/src/Commands/RebuildThumbnails.php +++ b/src/Commands/RebuildThumbnails.php @@ -7,6 +7,7 @@ use Exception; use Illuminate\Console\Command; use Intervention\Image\ImageManager; use Lucent\Channel\ChannelService; +use Lucent\File\FileRepo; use Lucent\File\FileService; use Lucent\Query\Query; use Lucent\Schema\FilesSchema; @@ -26,35 +27,16 @@ class RebuildThumbnails extends Command parent::__construct(); } - public function handle(ChannelService $channelService): int + public function handle(): void { - $channelService->channel->schemas - ->filter( - fn(Schema $schema) => get_class($schema) === FilesSchema::class, - ) - ->map([$this, "rebuildThumbnails"]); - - return 0; - } - - public function rebuildThumbnails(FilesSchema $schema): void - { - $this->info("Rebuilding thumbnails for " . $schema->name); - $records = $this->query - ->limit(0) - ->filter(["schema" => $schema->name]) - ->run()->records; - $disk = $this->fileService->loadDisk($schema->disk); - foreach ($records as $record) { + $this->info("Rebuilding thumbnails "); + $files = FileRepo::query()->get(); + $disk = $this->fileService->loadDisk(); + foreach ($files as $file) { try { - $this->fileService->createTemplates( - $disk, - $record->_file->path, - ); + $this->fileService->createTemplates($disk, $file->path); } catch (Exception $e) { - echo "File " . - $record->_file->originalName . - " could not be rebuilt \n"; + echo "File " . $file->filename . " could not be rebuilt \n"; } } } diff --git a/src/Commands/SetupDatabase.php b/src/Commands/SetupDatabase.php index 24e6138..39e05c8 100644 --- a/src/Commands/SetupDatabase.php +++ b/src/Commands/SetupDatabase.php @@ -71,19 +71,23 @@ class SetupDatabase extends Command ) { $table->uuid("id")->primary(); $table->string("schema"); + $table->integer("version"); $table->string("status"); $table->jsonb("data"); - $table->jsonb("_sys"); + $table->timestampTz("createdAt"); + $table->timestampTz("updatedAt"); + $table->string("createdBy"); + $table->string("updatedBy"); $table->text("search")->default(""); - // $table->index(["schema", "_sys->updatedAt", "status"]); + $table->index(["schema", "updatedAt", "status"]); $table->index("search"); }); - DB::statement( - "CREATE INDEX ON " . - $this->prefix . - 'records (schema, ((_sys->>\'updatedAt\')), status)', - ); + // DB::statement( + // "CREATE INDEX ON " . + // $this->prefix . + // 'records (schema, ((_sys->>\'updatedAt\')), status)', + // ); } if (!$schema->hasTable($this->prefix . "edges")) { @@ -140,8 +144,11 @@ class SetupDatabase extends Command $table->uuid("recordId"); $table->string("schema"); $table->jsonb("data"); - $table->jsonb("_sys"); - $table->jsonb("_file"); + $table->integer("version"); + $table->timestampTz("createdAt"); + $table->timestampTz("updatedAt"); + $table->string("createdBy"); + $table->string("updatedBy"); $table->jsonb("_edges"); }); } diff --git a/src/Commands/UpgradeFiles122.php b/src/Commands/UpgradeFiles122.php deleted file mode 100644 index 57d6e93..0000000 --- a/src/Commands/UpgradeFiles122.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php - -namespace Lucent\Commands; - -use Illuminate\Console\Command; -use Lucent\Database\Database; - -class UpgradeFiles122 extends Command -{ - - protected $signature = 'lucent:upgrade:files_1_2_2 {schema} {disk}'; - protected $description = 'Upgrade to the new filesystem'; - - - public function handle() - { - $schema = $this->argument('schema'); - $disk = $this->argument('disk'); - $db = Database::make(); - $records = $db->table("lucent_records")->where("schema", $schema)->get(); - foreach ($records as $record) { - $array = json_decode($record->_file, true); - $array["disk"] = $disk; - $db->table("lucent_records")->where("id", $record->id)->update(["_file" => json_encode($array)]); - } - } -} diff --git a/src/File/FileRepo.php b/src/File/FileRepo.php index fd579f4..4b289f9 100644 --- a/src/File/FileRepo.php +++ b/src/File/FileRepo.php @@ -2,6 +2,7 @@ namespace Lucent\File; +use Illuminate\Database\Query\Builder; use Lucent\Data\File as DataFile; use Lucent\Database\Database; use Lucent\Data\File; @@ -15,6 +16,11 @@ class FileRepo Database::make()->table("lucent_files")->insert($file->toDB()); } + public static function query(): Builder + { + return Database::make()->table("lucent_files"); + } + /** * @return File[] */ diff --git a/src/File/FileService.php b/src/File/FileService.php index a3fc151..52c3595 100644 --- a/src/File/FileService.php +++ b/src/File/FileService.php @@ -14,7 +14,6 @@ use Lucent\Id\Id; use Lucent\LucentException; use Lucent\Data\File as DataFile; use Lucent\Record\QueryRecord; -use Lucent\Schema\FilesSchema; use Spatie\ImageOptimizer\OptimizerChainFactory; class FileService @@ -25,15 +24,8 @@ class FileService public Logger $logger, ) {} - public function getPath(QueryRecord $file): string - { - return $this->channelService->channel->filesUrl . - "/" . - $file->_file->path; - } - public function createFromUrl( - FilesSchema $schema, + string $recordId, string $url, ): FileUploadResult { $pathinfo = pathinfo($url); @@ -44,7 +36,7 @@ class FileService $file = "/tmp/" . $pathinfo["basename"]; file_put_contents($file, $contents); $uploadedFile = new UploadedFile($file, $pathinfo["basename"]); - return $this->upload($schema, $uploadedFile); + return $this->upload($recordId, $uploadedFile); } public function upload(string $recordId, UploadedFile $file): DataFile @@ -130,21 +122,6 @@ class FileService return Storage::disk(config("lucent.disk")); } - private function checkDuplicate( - string $schemaName, - string $checksum, - int $filesize, - ): string { - $record = Database::make() - ->table("lucent_records") - ->where("schema", $schemaName) - ->where("_file->checksum", $checksum) - ->where("_file->size", $filesize) - ->first(); - - return $record->id ?? ""; - } - public function createTemplates(Filesystem $disk, string $path): void { $originalImage = $this->imageManager->make($disk->get($path)); diff --git a/src/Http/Controller/RecordController.php b/src/Http/Controller/RecordController.php index 24a0274..5cf9ed6 100644 --- a/src/Http/Controller/RecordController.php +++ b/src/Http/Controller/RecordController.php @@ -58,7 +58,6 @@ class RecordController extends Controller $users = $this->accountService->all(); $schema = $this->channelService->getSchema($schemaName)->get(); - $urlParams = $request->all(); $sort = data_get($urlParams, "sort") ?? $schema->sortBy; $filter = data_get($urlParams, "filter") ?? []; @@ -86,8 +85,8 @@ class RecordController extends Controller ->childrenFields($schema?->visible ?? []) ->childrenDepth(1) ->parentsDepth(0) - ->runWithCount(); + ->runWithCount(); $records = $graph->getRootRecords()->toArray(); $data = [ diff --git a/src/LucentServiceProvider.php b/src/LucentServiceProvider.php index 2fc4d1a..fc6ac22 100644 --- a/src/LucentServiceProvider.php +++ b/src/LucentServiceProvider.php @@ -89,8 +89,6 @@ class LucentServiceProvider extends ServiceProvider RemoveOrphanEdges::class, SetupDatabase::class, GenerateCollectionSchema::class, - GenerateFileSchema::class, - UpgradeFiles122::class, ]); } diff --git a/src/Query/Query.php b/src/Query/Query.php index 3b4edc3..47347a5 100644 --- a/src/Query/Query.php +++ b/src/Query/Query.php @@ -78,6 +78,7 @@ final class Query ->table("lucent_records") ->whereIn("id", $edgesIds) ->whereIn("status", $this->options->status) + ->get() ->toArray(); } @@ -295,7 +296,8 @@ final class Query public function orderByQuery(Builder $query): Builder { foreach ($this->options->sort as $item) { - $field = str_replace(".", "->", ltrim($item, "-")); + $field = str_replace("_sys.", "", ltrim($item, "-")); + $field = str_replace(".", "->", $field); $dir = str_starts_with($item, "-") ? "desc" : "asc"; if ($field) { $query->orderBy($field, $dir); diff --git a/src/Record/QueryRecord.php b/src/Record/QueryRecord.php index 07a9923..b7a80c2 100644 --- a/src/Record/QueryRecord.php +++ b/src/Record/QueryRecord.php @@ -2,24 +2,25 @@ namespace Lucent\Record; +use Carbon\Carbon; use Lucent\LucentException; class QueryRecord { - function __construct( - public string $id, - public string $schema, - public Status $status, - public System $_sys, + public string $id, + public string $schema, + public Status $status, public RecordData $data, - public bool $isRoot, - public ?FileData $_file = null, - public array $_children = [], - public array $_parents = [], - ) - { - } + public Carbon $createdAt, + public Carbon $updatedAt, + public string $createdBy, + public string $updatedBy, + public int $version, + public bool $isRoot, + public array $_children = [], + public array $_parents = [], + ) {} public static function fromRecord(Record $record): QueryRecord { @@ -27,10 +28,13 @@ class QueryRecord id: $record->id, schema: $record->schema, status: $record->status, - _sys: $record->_sys, data: $record->data, + createdAt: $record->createdAt, + updatedAt: $record->updatedAt, + createdBy: $record->createdBy, + updatedBy: $record->updatedBy, + version: $record->version, isRoot: false, - _file: $record->_file, ); } } diff --git a/src/Record/Record.php b/src/Record/Record.php index bcc1947..9603fe3 100644 --- a/src/Record/Record.php +++ b/src/Record/Record.php @@ -2,36 +2,46 @@ namespace Lucent\Record; +use Carbon\Carbon; use JsonSerializable; use stdClass; use Illuminate\Support\Str; class Record implements JsonSerializable { - - function __construct( - public string $id, - public string $schema, - public Status $status, - public System $_sys, + public string $id, + public string $schema, + public Status $status, + public Carbon $createdAt, + public Carbon $updatedAt, + public string $createdBy, + public string $updatedBy, + public int $version, public RecordData $data, - public ?FileData $_file = null, - ) + ) {} + + private function indexValues(array $arrObject) { - } - - - private function indexValues(array $arrObject){ - - return trim(Str::lower(collect($arrObject) - ->map(function($value){ - if(is_array($value)){ - return $this->indexValues($value ?? []); - } - return str_replace(array("\r", "\n"), '', strip_tags((string)$value)); - }) - ->values()->join(" ")." ". $this->_file?->originalName ?? "")); + return trim( + Str::lower( + collect($arrObject) + ->map(function ($value) { + if (is_array($value)) { + return $this->indexValues($value ?? []); + } + return str_replace( + ["\r", "\n"], + "", + strip_tags((string) $value), + ); + }) + ->values() + ->join(" ") . + " " . + "", + ), + ); } public function toDB(): array @@ -41,8 +51,11 @@ class Record implements JsonSerializable "id" => $this->id, "status" => $this->status->value, "schema" => $this->schema, - "_sys" => json_encode($this->_sys), - "_file" => json_encode($this->_file), + "createdAt" => $this->createdAt->toDateTimeString(), + "updatedAt" => $this->updatedAt->toDateTimeString(), + "createdBy" => $this->createdBy, + "updatedBy" => $this->updatedBy, + "version" => $this->version, "data" => json_encode($this->data), "search" => $searchIndex, ]; @@ -50,30 +63,21 @@ class Record implements JsonSerializable public static function fromDB(stdClass $data): Record { - - $file = json_decode($data->_file, true); - if (!empty($file)) { - - $file = FileData::fromArray($file); - } else { - $file = null; - } - return new Record( id: $data->id, schema: $data->schema, status: Status::from($data->status), - _sys: System::fromArray(json_decode($data->_sys, true)), + createdAt: Carbon::parse($data->createdAt), + updatedAt: Carbon::parse($data->updatedAt), + createdBy: $data->createdBy, + updatedBy: $data->updatedBy, + version: $data->version, data: new RecordData(json_decode($data->data, true)), - _file: $file, ); } - public function jsonSerialize(): static { return $this; } - - } diff --git a/src/Record/RecordService.php b/src/Record/RecordService.php index c39cb64..a55722d 100644 --- a/src/Record/RecordService.php +++ b/src/Record/RecordService.php @@ -2,6 +2,7 @@ namespace Lucent\Record; +use Carbon\Carbon; use Illuminate\Http\UploadedFile; use Illuminate\Support\Str; use Lucent\Account\AuthService; @@ -16,7 +17,7 @@ use Lucent\Record\InputData\EdgeInputData; use Lucent\Record\InputData\RecordInputData; use Lucent\Revision\RevisionService; use Lucent\Schema\FieldInterface; -use Lucent\Schema\Schema; +use Lucent\Data\Schema; use Lucent\Schema\Type; use Lucent\Schema\Validator\Validator; use Lucent\Schema\Validator\ValidatorException; @@ -115,9 +116,12 @@ readonly class RecordService id: $newRecordId, schema: $data->schemaName, status: $data->status, - _sys: System::newRecord($this->authService->currentUserId()), + version: 1, + createdBy: $this->authService->currentUserId(), + updatedBy: $this->authService->currentUserId(), + createdAt: Carbon::now(), + updatedAt: Carbon::now(), data: $formattedData, - _file: !empty($file) ? FileData::fromArray(toArray($file)) : null, ); if ($data->status === Status::PUBLISHED) { @@ -173,9 +177,12 @@ readonly class RecordService id: $record->id, schema: $record->schema, status: $status, - _sys: $record->_sys->update($this->authService->currentUserId()), + version: $record->version + 1, + createdBy: $record->createdBy, + updatedBy: $this->authService->currentUserId(), + createdAt: $record->createdAt, + updatedAt: Carbon::now(), data: $record->data->merge($formattedData), - _file: $record->_file, ); RecordRepo::update($newRecord); @@ -205,12 +212,12 @@ readonly class RecordService $record->schema, new RecordData($data), ); - if ($status === Status::PUBLISHED) { $errors = $this->recordValidator->check( $record->schema, $formattedData, ); + if ($errors->isNotEmpty()) { $this->recordValidator->throwException($errors); } @@ -220,9 +227,12 @@ readonly class RecordService id: $record->id, schema: $record->schema, status: $status, - _sys: $record->_sys->update($this->authService->currentUserId()), + version: $record->version + 1, + createdBy: $record->createdBy, + updatedBy: $this->authService->currentUserId(), + createdAt: $record->createdAt, + updatedAt: Carbon::now(), data: $record->data->merge($formattedData), - _file: $record->_file, ); RecordRepo::update($newRecord); @@ -278,7 +288,6 @@ readonly class RecordService data: $record->data->toArray(), status: Status::DRAFT, ), - file: $record->_file, edges: $newEdgesData, ); } @@ -337,9 +346,12 @@ readonly class RecordService id: Id::new(), schema: $schema->name, status: Status::DRAFT, - _sys: System::newRecord($this->authService->currentUserId()), + version: 1, + createdBy: $this->authService->currentUserId(), + updatedBy: $this->authService->currentUserId(), + createdAt: Carbon::now(), + updatedAt: Carbon::now(), data: $formattedData, - _file: null, ); } } diff --git a/src/Record/System.php b/src/Record/System.php deleted file mode 100644 index ddff392..0000000 --- a/src/Record/System.php +++ /dev/null @@ -1,62 +0,0 @@ -<?php - -namespace Lucent\Record; - -use Carbon\Carbon; -use Lucent\Schema\Schema; - -readonly class System -{ - - - function __construct( - public int $version, - public string $createdBy, - public string $updatedBy, - public string $createdAt, - public string $updatedAt, - ) - { - - } - - - public static function fromArray(array $data): System - { - return new System( - version: data_get($data, "version"), - createdBy: data_get($data, "createdBy"), - updatedBy: data_get($data, "updatedBy"), - createdAt: data_get($data, "createdAt"), - updatedAt: data_get($data, "updatedAt"), - ); - } - - - public static function newRecord(string $userId): System - { - $now = Carbon::now()->toJson(); - return new System( - version: 1, - createdBy: $userId, - updatedBy: $userId, - createdAt: $now, - updatedAt: $now, - ); - } - - - public function update(string $userId): System - { - $now = Carbon::now()->toJson(); - return new System( - version: $this->version + 1, - createdBy: $this->createdBy, - updatedBy: $userId, - createdAt: $this->createdAt, - updatedAt: $now, - ); - } - - -} diff --git a/src/Revision/Revision.php b/src/Revision/Revision.php index 66754a5..896dba0 100644 --- a/src/Revision/Revision.php +++ b/src/Revision/Revision.php @@ -2,51 +2,47 @@ namespace Lucent\Revision; +use Carbon\Carbon; use Illuminate\Support\Str; use Lucent\Edge\Edge; -use Lucent\Record\FileData; use Lucent\Record\Record; use Lucent\Record\RecordData; -use Lucent\Record\System; readonly class Revision { - /** * @param string $id * @param string $recordId * @param string $schema - * @param System $_sys * @param RecordData $data * @param list<Edge> $_edges - * @param FileData|null $_file */ function __construct( - public string $id, - public string $recordId, - public string $schema, - public System $_sys, + public string $id, + public string $recordId, + public string $schema, + public Carbon $createdAt, + public Carbon $updatedAt, + public string $createdBy, + public string $updatedBy, + public int $version, public RecordData $data, - public array $_edges, - public ?FileData $_file = null, - - ) - { - } - + public array $_edges, + ) {} public static function fromRecord(Record $record, array $edges): Revision { return new Revision( - id: (string)Str::uuid(), + id: (string) Str::uuid(), recordId: $record->id, schema: $record->schema, - _sys: $record->_sys, + createdAt: $record->createdAt, + updatedAt: $record->updatedAt, + createdBy: $record->createdBy, + updatedBy: $record->updatedBy, + version: $record->version, data: $record->data, _edges: $edges, - _file: $record->_file ); } - - } diff --git a/src/Revision/RevisionRepo.php b/src/Revision/RevisionRepo.php index bbac73f..6156479 100644 --- a/src/Revision/RevisionRepo.php +++ b/src/Revision/RevisionRepo.php @@ -5,15 +5,12 @@ namespace Lucent\Revision; use Lucent\Database\Database; use Lucent\Edge\Edge; use Lucent\Primitive\Collection; -use Lucent\Record\FileData; use Lucent\Record\RecordData; -use Lucent\Record\System; use PhpOption\Option; use stdClass; class RevisionRepo { - private string $table = "lucent_revisions"; public function create(Revision $revision): string @@ -23,17 +20,17 @@ class RevisionRepo return $revision->id; } - /** * @return Collection<Revision> **/ public function getByRecordId(string $rid): Collection { - $revisions = Database::make()->table($this->table) + $revisions = Database::make() + ->table($this->table) ->where("recordId", $rid) ->get() - ->map([$this, 'fromDB']) - ->sortByDesc("_sys.version") + ->map([$this, "fromDB"]) + ->sortByDesc("version") ->toArray(); return new Collection($revisions); @@ -41,29 +38,31 @@ class RevisionRepo public function cleanupRecord(string $rid, int $numKeep): void { - $revisionIds = Database::make()->table($this->table) + $revisionIds = Database::make() + ->table($this->table) ->where("recordId", $rid) - ->orderBy("_sys->version", "desc") + ->orderBy("version", "desc") ->limit(100) ->skip($numKeep) ->get() ->pluck("id"); - Database::make()->table($this->table) + Database::make() + ->table($this->table) ->whereIn("id", $revisionIds) ->delete(); } - /** * @return Option<Revision> */ public function getByRecordIdAndVersion(string $rid, int $version): Option { - - $res = Database::make()->table($this->table) + $res = Database::make() + ->table($this->table) ->where("recordId", $rid) - ->where('_sys->version', $version)->first(); + ->where("version", $version) + ->first(); if (empty($res)) { return none(); @@ -78,8 +77,11 @@ class RevisionRepo "id" => $revision->id, "recordId" => $revision->recordId, "schema" => $revision->schema, - "_sys" => json_encode($revision->_sys), - "_file" => json_encode($revision->_file), + "createdAt" => $revision->createdAt->toDateTimeString(), + "updatedAt" => $revision->updatedAt->toDateTimeString(), + "createdBy" => $revision->createdBy, + "updatedBy" => $revision->updatedBy, + "version" => $revision->version, "data" => json_encode($revision->data), "_edges" => json_encode($revision->_edges), ]; @@ -87,24 +89,22 @@ class RevisionRepo public function fromDB(stdClass $data): Revision { - $file = json_decode($data->_file, true); - if (!empty($file)) { - - $file = FileData::fromArray($file); - } else { - $file = null; - } - - $edges = array_map(fn($e) => Edge::fromArray($e), json_decode($data->_edges ?? "[]", true)); + $edges = array_map( + fn($e) => Edge::fromArray($e), + json_decode($data->_edges ?? "[]", true), + ); return new Revision( id: $data->id, recordId: $data->recordId, schema: $data->schema, - _sys: System::fromArray(json_decode($data->_sys, true)), + createdBy: $data->createdBy, + updatedBy: $data->updatedBy, + createdAt: $data->createdAt, + updatedAt: $data->updatedAt, + version: $data->version, data: new RecordData(json_decode($data->data, true)), _edges: $edges, - _file: $file ); } } diff --git a/src/Schema/Nullable.php b/src/Schema/Nullable.php index 754406b..954169f 100644 --- a/src/Schema/Nullable.php +++ b/src/Schema/Nullable.php @@ -5,16 +5,21 @@ namespace Lucent\Schema; final class Nullable { public function __construct( - public bool $nullable, + public bool $nullable, public mixed $value, public mixed $default, - ) - { + ) {} + + public static function make( + bool $nullable, + mixed $value, + mixed $default, + ): Nullable { + return new self($nullable, $value, $default); } public function value(): mixed { - if (!empty($this->value)) { return $this->value; } diff --git a/src/Schema/SchemaService.php b/src/Schema/SchemaService.php index f6a7c8e..841ff81 100644 --- a/src/Schema/SchemaService.php +++ b/src/Schema/SchemaService.php @@ -12,6 +12,16 @@ class SchemaService public function fromArray(array $schemaArr): Schema { + $schemaArr["fields"] = [ + [ + "ui" => "text", + "name" => "name", + "label" => "Name", + "required" => true, + ], + ...$schemaArr["fields"], + ]; + return new Schema( name: $schemaArr["name"], label: $schemaArr["label"], diff --git a/src/Schema/System.php b/src/Schema/System.php index ba44a63..2e857f3 100644 --- a/src/Schema/System.php +++ b/src/Schema/System.php @@ -8,11 +8,8 @@ readonly class System public string $name, public string $label, public string $ui, - public bool $files, - ) - { - } - + public bool $files, + ) {} public static function getById(string $id): System { @@ -26,7 +23,7 @@ readonly class System { return [ "_sys.status" => new System( - name: "status", + name: "_sys.status", label: "Status", ui: "status", files: false, @@ -55,45 +52,6 @@ readonly class System ui: "datetime", files: false, ), - "_file.path" => new System( - name: "_file.path", - label: "Filename", - ui: "text", - files: true, - ), - "_file.mime" => new System( - name: "_file.mime", - label: "Mime Type", - ui: "text", - files: true, - ), - - "_file.width" => new System( - name: "_file.width", - label: "Dimensions", - ui: "text", - files: true, - ), - "_file.size" => new System( - name: "_file.size", - label: "Size", - ui: "text", - files: true, - ), - "_file.originalName" => new System( - name: "_file.originalName", - label: "Original Name", - ui: "text", - files: true, - ), - "_file.checksum" => new System( - name: "_file.checksum", - label: "Checksum", - ui: "text", - files: true, - ), - - ]; } } diff --git a/src/Schema/Ui/File.php b/src/Schema/Ui/File.php index 68514a0..7d2ebf9 100644 --- a/src/Schema/Ui/File.php +++ b/src/Schema/Ui/File.php @@ -11,7 +11,6 @@ class File implements FieldInterface, MinMaxInterface { public FieldInfo $info; - /** * @param string[] $collections */ @@ -20,17 +19,18 @@ class File implements FieldInterface, MinMaxInterface public string $label, public string $mime = "", public string $help = "", - public ?int $min = null, - public ?int $max = null, - public array $collections = [], + public ?int $min = null, + public ?int $max = null, + public array $collections = [], public string $group = "", - ) - { + ) { $this->info = new FieldInfo("file", "File", FieldType::FILE); } public function format(array $input, array $output): array { + $value = $input[$this->name] ?? []; + $output[$this->name] = $value; return $output; } @@ -51,6 +51,4 @@ class File implements FieldInterface, MinMaxInterface return count($value) < $this->min; } - } - diff --git a/src/Schema/Ui/Slug.php b/src/Schema/Ui/Slug.php index fbf8bd1..1c41afc 100644 --- a/src/Schema/Ui/Slug.php +++ b/src/Schema/Ui/Slug.php @@ -16,35 +16,38 @@ class Slug implements FieldInterface, RequiredInterface public function __construct( public string $name, public string $label, - public bool $required = false, - public bool $nullable = false, - public ?int $min = null, - public ?int $max = null, + public bool $required = false, + public bool $nullable = false, + public ?int $min = null, + public ?int $max = null, public string $default = "", public string $help = "", - public bool $readonly = false, + public bool $readonly = false, public string $source = "", public string $group = "", - ) - { + ) { $this->info = new FieldInfo("slug", "Slug", FieldType::STRING); } public function format(array $input, array $output): array { - $value = !empty($input[$this->name]) ? (string)$input[$this->name] : null; - if(empty($value)){ + $value = !empty($input[$this->name]) + ? (string) $input[$this->name] + : null; + if (empty($value)) { $value = Str::slug($input[$this->source]); } - $output[$this->name] = (new Nullable($this->nullable, $value, ""))->value(); + $output[$this->name] = new Nullable( + $this->nullable, + $value, + "", + )->value(); return $output; } - public function failRequired(mixed $value): bool { return empty(trim($value)); } } - diff --git a/src/macros.php b/src/macros.php index 0931762..8544307 100644 --- a/src/macros.php +++ b/src/macros.php @@ -3,7 +3,7 @@ use PhpOption\None; use PhpOption\Some; -if (!function_exists('some')) { +if (!function_exists("some")) { /** * @template T * @param T $value @@ -15,51 +15,50 @@ if (!function_exists('some')) { } } - -if (!function_exists('none')) { +if (!function_exists("none")) { function none(): None { return None::create(); } } -if (!function_exists('toArray')) { +if (!function_exists("toArray")) { function toArray(mixed $data): array { return \json_decode(\json_encode($data), true); } } - -if (!function_exists('make_dir_r')) { +if (!function_exists("make_dir_r")) { function make_dir_r(string $path): void { is_dir($path) || mkdir($path, 0777, true); } } - -if (!function_exists('schemas_path')) { +if (!function_exists("schemas_path")) { function schemas_path(): string { return storage_path("lucent/lucent.schemas.json"); } } -if (!function_exists('lucent_file')) { - function lucent_file(\Lucent\Record\QueryRecord $record): string +if (!function_exists("lucent_file")) { + function lucent_file(\Lucent\Data\File $file): string { - $path = $record->_file->path; - return app()->make(\Lucent\Channel\ChannelService::class)->channel->disks[$record->_file->disk] ."/". $path; + $path = $file->path; + return app()->make(\Lucent\Channel\ChannelService::class)->channel + ->filesUrl . + "/" . + $path; } } -if (!function_exists('lucent_image')) { - function lucent_image(\Lucent\Record\QueryRecord $record, string $template): string +if (!function_exists("lucent_image")) { + function lucent_image(\Lucent\Data\File $file, string $template): string { - $path = $record->_file->path; - return app()->make(\Lucent\Channel\ChannelService::class)->channel->disks[$record->_file->disk] . "/templates/$template/$path"; + $path = $file->path; + return app()->make(\Lucent\Channel\ChannelService::class)->channel + ->filesUrl . "/templates/$template/$path"; } } - -