crazy stuff
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"project_name": "Lucent Package",
|
||||
"languages": {
|
||||
"PHP": {
|
||||
"language_servers": ["phpactor"],
|
||||
},
|
||||
},
|
||||
}
|
||||
+2
-1
@@ -15,7 +15,8 @@
|
||||
"phpoption/phpoption": "^1.9",
|
||||
"spatie/image-optimizer": "^1.6",
|
||||
"staudenmeir/laravel-cte": "^1.0",
|
||||
"mustache/mustache": "^2.14"
|
||||
"mustache/mustache": "^2.14",
|
||||
"yosymfony/toml": "^1.0"
|
||||
|
||||
},
|
||||
"require-dev": {
|
||||
|
||||
Generated
+111
-1
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "e8fb1bee28ad339453d50110f7fea2f5",
|
||||
"content-hash": "93994bb8d362147341e2fed15f80b93f",
|
||||
"packages": [
|
||||
{
|
||||
"name": "brick/math",
|
||||
@@ -5657,6 +5657,116 @@
|
||||
"source": "https://github.com/webmozarts/assert/tree/1.11.0"
|
||||
},
|
||||
"time": "2022-06-03T18:03:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "yosymfony/parser-utils",
|
||||
"version": "v2.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/yosymfony/parser-utils.git",
|
||||
"reference": "00bec9a12722b21f2baf7f9db35f127e90c162c9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/yosymfony/parser-utils/zipball/00bec9a12722b21f2baf7f9db35f127e90c162c9",
|
||||
"reference": "00bec9a12722b21f2baf7f9db35f127e90c162c9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^6"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Yosymfony\\ParserUtils\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Victor Puertas",
|
||||
"email": "vpgugr@gmail.com",
|
||||
"homepage": "http://yosymfony.com"
|
||||
}
|
||||
],
|
||||
"description": "Parser utilities",
|
||||
"homepage": "http://github.com/yosymfony/toml",
|
||||
"keywords": [
|
||||
"lexer",
|
||||
"parser"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/yosymfony/parser-utils/issues",
|
||||
"source": "https://github.com/yosymfony/parser-utils/tree/master"
|
||||
},
|
||||
"time": "2018-06-29T15:31:11+00:00"
|
||||
},
|
||||
{
|
||||
"name": "yosymfony/toml",
|
||||
"version": "v1.0.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/yosymfony/toml.git",
|
||||
"reference": "bdab92ad920d0e36810a3a3e4a998d23f3498f8e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/yosymfony/toml/zipball/bdab92ad920d0e36810a3a3e4a998d23f3498f8e",
|
||||
"reference": "bdab92ad920d0e36810a3a3e4a998d23f3498f8e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1",
|
||||
"yosymfony/parser-utils": "^2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7.1"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Yosymfony\\Toml\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Victor Puertas",
|
||||
"email": "vpgugr@gmail.com",
|
||||
"homepage": "http://yosymfony.com"
|
||||
}
|
||||
],
|
||||
"description": "A PHP parser for TOML compatible with specification 0.4.0",
|
||||
"homepage": "http://github.com/yosymfony/toml",
|
||||
"keywords": [
|
||||
"mojombo",
|
||||
"parser",
|
||||
"toml"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/yosymfony/toml/issues",
|
||||
"source": "https://github.com/yosymfony/toml/tree/master"
|
||||
},
|
||||
"time": "2018-08-08T15:08:14+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
@import "./pico.min.css";
|
||||
|
||||
main {
|
||||
display: flex;
|
||||
gap: 40px;
|
||||
aside {
|
||||
width: 250px;
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
Vendored
+4
File diff suppressed because one or more lines are too long
@@ -0,0 +1,36 @@
|
||||
<script>
|
||||
import { getContext, onMount } from "svelte";
|
||||
import RecordRow from "./RecordRow.svelte";
|
||||
import ChannelLayout from "../../layouts/ChannelLayout.svelte";
|
||||
import { get } from "../../modules/remote";
|
||||
let { channel, user } = $props();
|
||||
let records = $state([]);
|
||||
let graph = $state(null);
|
||||
let users = $state([]);
|
||||
onMount(() => {
|
||||
get(channel.lucentUrl + "/home/records", {}, (data) => {
|
||||
records = data.records;
|
||||
graph = data.graph;
|
||||
users = data.users;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<ChannelLayout {body} {channel} {user}></ChannelLayout>
|
||||
{#snippet body()}
|
||||
<h3 class="header-small mb-4 mt-5">Latest Content changes</h3>
|
||||
{#if records.length > 0}
|
||||
<div class="table">
|
||||
<table class="">
|
||||
<tbody>
|
||||
{#each records as record (record.id)}
|
||||
<tr>
|
||||
<RecordRow {channel} {graph} {record} {users}
|
||||
></RecordRow>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{/if}
|
||||
{/snippet}
|
||||
@@ -0,0 +1,41 @@
|
||||
<script>
|
||||
import { formatDistanceToNow, parseJSON } from "date-fns";
|
||||
import Avatar from "../../svelte/account/Avatar.svelte";
|
||||
import { previewTitle } from "../../svelte/records/Preview";
|
||||
import Preview from "../../svelte/files/Preview.svelte";
|
||||
import { usernameById } from "../../svelte/account/users";
|
||||
|
||||
let { channel, users, record, graph } = $props();
|
||||
let schema = $derived(
|
||||
channel.schemas.find((s) => s.name === record.schema),
|
||||
);
|
||||
let frieldlyUpdatedAt = formatDistanceToNow(
|
||||
parseJSON(record._sys.updatedAt),
|
||||
{ addSuffix: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
<td>
|
||||
<div class="row-name">
|
||||
{#if record.status === "draft"}
|
||||
<span class="status">DRAFT</span>
|
||||
{/if}
|
||||
{#if schema.type === "files"}
|
||||
<!-- <Preview {record} size="tiny" showFilename={true} /> -->
|
||||
{:else}
|
||||
<a href="{channel.lucentUrl}/records/{record.id}">
|
||||
<!-- {previewTitle(channel.schemas, record, graph)} -->
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
</td>
|
||||
<td><a href="{channel.lucentUrl}/content/{schema.name}">{schema.label}</a> </td>
|
||||
|
||||
<td>
|
||||
<div style="display: flex;gap: 14px">
|
||||
<Avatar name={usernameById(users, record._sys.updatedBy)} side={24} />
|
||||
<div class="ms-2">
|
||||
{frieldlyUpdatedAt}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
@@ -0,0 +1,15 @@
|
||||
<script>
|
||||
import Header from "./Header.svelte";
|
||||
import Navbar from "./Navbar.svelte";
|
||||
let { body, channel, user } = $props();
|
||||
</script>
|
||||
|
||||
<Header {channel} {user}></Header>
|
||||
<main>
|
||||
<aside class="sidebar-content">
|
||||
<Navbar {channel}></Navbar>
|
||||
</aside>
|
||||
<div class="main-content">
|
||||
{@render body()}
|
||||
</div>
|
||||
</main>
|
||||
@@ -1,26 +1,23 @@
|
||||
<script>
|
||||
import Avatar from "../account/Avatar.svelte";
|
||||
import {getContext} from "svelte";
|
||||
import Dropdown from "../common/Dropdown.svelte";
|
||||
|
||||
const channel = getContext("channel");
|
||||
const user = getContext("user");
|
||||
console.log( channel.commands)
|
||||
import Avatar from "../common/Avatar.svelte";
|
||||
// import Dropdown from "../svelte/common/Dropdown.svelte";
|
||||
let { channel, user } = $props();
|
||||
</script>
|
||||
|
||||
|
||||
<div class="top-nav ">
|
||||
<div class="top-nav">
|
||||
<a class="top-nav-item" href="{channel.lucentUrl}/members">Members</a>
|
||||
|
||||
{#if channel.commands.length > 0}
|
||||
<!-- {#if channel.commands.length > 0}
|
||||
<Dropdown>
|
||||
<div slot="button">Actions</div>
|
||||
{#each channel.commands as command}
|
||||
<a href="{channel.lucentUrl}/command-report/{command.signature}" class="top-nav-item">{command.name}</a>
|
||||
<a
|
||||
href="{channel.lucentUrl}/command-report/{command.signature}"
|
||||
class="top-nav-item">{command.name}</a
|
||||
>
|
||||
{/each}
|
||||
</Dropdown>
|
||||
|
||||
{/if}
|
||||
{/if} -->
|
||||
<!-- <div>-->
|
||||
<!-- <form method="GET">-->
|
||||
<!-- <input type="search" name="filter[search_regex]" placeholder="Search"-->
|
||||
@@ -28,7 +25,6 @@
|
||||
<!-- </form>-->
|
||||
<!-- </div>-->
|
||||
<a href="{channel.lucentUrl}/profile">
|
||||
<Avatar side="28" name={user.name}/>
|
||||
<Avatar side="28" name={user.name} />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<script>
|
||||
let { channel } = $props();
|
||||
</script>
|
||||
|
||||
<div class="sidebar-top">
|
||||
<a class="logo" href={channel.lucentUrl}>{channel.name}</a>
|
||||
<a class="nav-item" href="{channel.lucentUrl}/profile"> </a>
|
||||
</div>
|
||||
<div class="sidebar"></div>
|
||||
+49
-39
@@ -1,54 +1,64 @@
|
||||
import {axiosInstance} from "./bootstrap";
|
||||
import "../sass/app.scss";
|
||||
import Account from "./svelte/Account.svelte";
|
||||
import Channel from "./svelte/Channel.svelte";
|
||||
import Mustache from "mustache";
|
||||
import 'htmx.org';
|
||||
import { axiosInstance } from "./bootstrap";
|
||||
import { mount, setContext, unmount } from "svelte";
|
||||
|
||||
Mustache.escape = function (value) {
|
||||
return value;
|
||||
};
|
||||
// import "../sass/app.scss";
|
||||
import "../css/app.css";
|
||||
import Register from "./svelte/account/Register.svelte";
|
||||
import Login from "./svelte/account/Login.svelte";
|
||||
import Verify from "./svelte/account/Verify.svelte";
|
||||
import Profile from "./svelte/account/Profile.svelte";
|
||||
import SetupIndex from "./svelte/setup/Index.svelte";
|
||||
import Members from "./svelte/members/Members.svelte";
|
||||
import RecordNotFound from "./svelte/records/NotFound.svelte";
|
||||
import RecordEdit from "./svelte/records/Edit.svelte";
|
||||
import ContentIndex from "./svelte/content/Index.svelte";
|
||||
import HomeEntry from "./entry/HomeEntry/HomeEntry.svelte";
|
||||
import BuildReport from "./svelte/build/Report.svelte";
|
||||
|
||||
|
||||
// Define all components
|
||||
const entryComponents = {
|
||||
account: Account,
|
||||
channel: Channel,
|
||||
members: Members,
|
||||
recordEdit: RecordEdit,
|
||||
recordNotFound: RecordNotFound,
|
||||
contentIndex: ContentIndex,
|
||||
homeIndex: HomeEntry,
|
||||
buildReport: BuildReport,
|
||||
register: Register,
|
||||
login: Login,
|
||||
verify: Verify,
|
||||
profile: Profile,
|
||||
setup: SetupIndex,
|
||||
};
|
||||
|
||||
let loadedComponents = [];
|
||||
|
||||
let loadSvelte = function () {
|
||||
loadedComponents.map((comp) => comp.$destroy());
|
||||
loadedComponents = [];
|
||||
loadedComponents.map((comp) => unmount(comp));
|
||||
loadedComponents = [];
|
||||
|
||||
const elements = document.body.querySelectorAll(".lucent-component");
|
||||
if (elements.length === 0) {
|
||||
return;
|
||||
const elements = document.body.querySelectorAll(".lucent-component");
|
||||
if (elements.length === 0) {
|
||||
return;
|
||||
}
|
||||
const loadElement = function (element) {
|
||||
const jsonData = document.getElementById("json-data").innerHTML;
|
||||
|
||||
const props = JSON.parse(jsonData);
|
||||
const [__, view] = Object.entries(entryComponents).find(
|
||||
([key, _]) => props.view === key,
|
||||
);
|
||||
|
||||
if (!view) {
|
||||
return [];
|
||||
}
|
||||
const loadElement = function (element) {
|
||||
const componentId = element.attributes["data-layout"].value;
|
||||
const [_, component] = Object.entries(entryComponents).find(
|
||||
([key, _]) => componentId === key
|
||||
);
|
||||
if (!component) {
|
||||
return [];
|
||||
}
|
||||
// props.axios = axiosInstance;
|
||||
|
||||
const jsonData = document.getElementById(
|
||||
"json-" + componentId
|
||||
).innerHTML;
|
||||
const props = JSON.parse(jsonData);
|
||||
props.axios = axiosInstance;
|
||||
const compOptions = {
|
||||
target: element,
|
||||
props: props,
|
||||
};
|
||||
|
||||
loadedComponents = [...loadedComponents, new component(compOptions)];
|
||||
const compOptions = {
|
||||
target: element,
|
||||
props: props,
|
||||
};
|
||||
Array.from(elements).map(loadElement);
|
||||
loadedComponents = [...loadedComponents, mount(view, compOptions)];
|
||||
};
|
||||
Array.from(elements).map(loadElement);
|
||||
};
|
||||
|
||||
// document.addEventListener("turbo:load", loadSvelte);
|
||||
document.addEventListener("DOMContentLoaded", loadSvelte);
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
import axios from "axios";
|
||||
|
||||
class Errors {
|
||||
constructor(data) {
|
||||
this.data = data ?? [];
|
||||
}
|
||||
|
||||
first() {
|
||||
return this.data[0] ?? null;
|
||||
}
|
||||
|
||||
all() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
isEmpty() {
|
||||
return this.data.length === 0;
|
||||
}
|
||||
|
||||
isNotEmpty() {
|
||||
return !this.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
function makeErrors(errs) {
|
||||
return new Errors(errs);
|
||||
}
|
||||
|
||||
export function post(url, postData, callback) {
|
||||
axios
|
||||
.post(url, postData)
|
||||
.then((res) => {
|
||||
if (res.data.redirect !== undefined) {
|
||||
// Turbo.visit(link(res.data.redirect));
|
||||
return;
|
||||
}
|
||||
const errors = makeErrors(res.data.errors);
|
||||
if (errors.isNotEmpty()) {
|
||||
callback?.(null, errors);
|
||||
} else {
|
||||
callback?.(res.data, errors);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
const errors = makeErrors(["something went wrong"]);
|
||||
callback?.(null, errors);
|
||||
});
|
||||
}
|
||||
|
||||
export function get(url, urlParams, callback) {
|
||||
let params = url.endsWith("query")
|
||||
? {
|
||||
params: { query: JSON.stringify(urlParams) },
|
||||
paramsSerializer: (params) => serializeParams(params),
|
||||
}
|
||||
: {
|
||||
params: urlParams,
|
||||
};
|
||||
|
||||
return axios
|
||||
.get(url, params)
|
||||
.then((res) => {
|
||||
callback(res.data);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
let errors = makeErrors(["something went wrong"]);
|
||||
if (!err) {
|
||||
errors = makeErrors([]);
|
||||
}
|
||||
callback(null, errors);
|
||||
});
|
||||
}
|
||||
|
||||
function serializeParams(obj, prefix = "") {
|
||||
const params = [];
|
||||
|
||||
for (const key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
const value = obj[key];
|
||||
const paramKey = prefix ? `${prefix}[${key}]` : key;
|
||||
|
||||
if (
|
||||
typeof value === "object" &&
|
||||
value !== null &&
|
||||
!Array.isArray(value)
|
||||
) {
|
||||
// Nested object
|
||||
params.push(serializeParams(value, paramKey));
|
||||
} else if (Array.isArray(value)) {
|
||||
// Array
|
||||
value.forEach((item, index) => {
|
||||
if (typeof item === "object" && item !== null) {
|
||||
params.push(serializeParams(item, `${paramKey}[${index}]`));
|
||||
} else {
|
||||
params.push(
|
||||
`${encodeURIComponent(`${paramKey}[${index}]`)}=${encodeURIComponent(item)}`,
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Simple value
|
||||
params.push(
|
||||
`${encodeURIComponent(paramKey)}=${encodeURIComponent(value)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return params.flat().join("&");
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
<script>
|
||||
import { createEventDispatcher } from "svelte";
|
||||
const dispatch = createEventDispatcher();
|
||||
import { range } from "lodash";
|
||||
import NavItem from "./NavItem.svelte";
|
||||
export let inModal;
|
||||
export let modalUrl;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
<script>
|
||||
import {createEventDispatcher, getContext} from "svelte";
|
||||
import {debounce} from "lodash";
|
||||
import {previewTitle} from "../../records/Preview";
|
||||
|
||||
import { createEventDispatcher, getContext } from "svelte";
|
||||
import { previewTitle } from "../../records/Preview";
|
||||
|
||||
const channel = getContext("channel");
|
||||
const dispatch = createEventDispatcher();
|
||||
@@ -10,9 +8,8 @@
|
||||
export let value = "";
|
||||
export let field;
|
||||
|
||||
let search = ""
|
||||
$: searchOptions = []
|
||||
|
||||
let search = "";
|
||||
$: searchOptions = [];
|
||||
|
||||
const updateResults = debounce((e) => {
|
||||
axios
|
||||
@@ -35,46 +32,36 @@
|
||||
|
||||
function apply(e, newOption) {
|
||||
e.preventDefault();
|
||||
value = newOption.id
|
||||
value = newOption.id;
|
||||
dispatch("addFilter");
|
||||
value = ""
|
||||
|
||||
value = "";
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<div class="reference-tags">
|
||||
<input
|
||||
type="search"
|
||||
on:keyup={updateResults}
|
||||
bind:value={search}
|
||||
placeholder={"Search for "+field.label}
|
||||
autocomplete="off"
|
||||
type="search"
|
||||
on:keyup={updateResults}
|
||||
bind:value={search}
|
||||
placeholder={"Search for " + field.label}
|
||||
autocomplete="off"
|
||||
/>
|
||||
|
||||
<div class="reference-tags-results">
|
||||
|
||||
{#if searchOptions}
|
||||
{#each searchOptions as option (option.id)}
|
||||
<div
|
||||
class="reference-tags-option"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
on:click={(e) => apply(e, option)}
|
||||
on:keypress={(e) => apply(e, option)}
|
||||
class="reference-tags-option"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
on:click={(e) => apply(e, option)}
|
||||
on:keypress={(e) => apply(e, option)}
|
||||
>
|
||||
{previewTitle(channel.schemas, option)}
|
||||
</div>
|
||||
|
||||
{:else}
|
||||
<div
|
||||
class="start-typing">
|
||||
Start typing...
|
||||
</div>
|
||||
<div class="start-typing">Start typing...</div>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
<script>
|
||||
|
||||
import {getContext, onMount} from "svelte";
|
||||
import RecordRow from "./RecordRow.svelte"
|
||||
|
||||
const channel = getContext("channel");
|
||||
let records = [];
|
||||
let graph = null;
|
||||
let users = [];
|
||||
onMount(() => {
|
||||
axios
|
||||
.get(channel.lucentUrl + "/home/records")
|
||||
.then((response) => {
|
||||
records = response.data.records;
|
||||
graph = response.data.graph;
|
||||
users = response.data.users;
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
<h3 class="header-small mb-4 mt-5">Latest Content changes</h3>
|
||||
{#if records.length > 0}
|
||||
|
||||
<div class="table">
|
||||
<table class="">
|
||||
<tbody>
|
||||
{#each records as record (record.id)}
|
||||
<tr>
|
||||
<RecordRow {graph} {record} {users}/>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/if}
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
<script>
|
||||
import {formatDistanceToNow, parseJSON} from "date-fns";
|
||||
import Avatar from "../account/Avatar.svelte";
|
||||
import {previewTitle} from "../records/Preview";
|
||||
import Preview from "../files/Preview.svelte";
|
||||
import {usernameById} from "../account/users";
|
||||
import {getContext} from "svelte";
|
||||
|
||||
const channel = getContext("channel");
|
||||
export let users;
|
||||
export let graph;
|
||||
export let record;
|
||||
let schema = channel.schemas.find((s) => s.name === record.schema);
|
||||
let frieldlyUpdatedAt = formatDistanceToNow(
|
||||
parseJSON(record._sys.updatedAt),
|
||||
{addSuffix: true}
|
||||
);
|
||||
</script>
|
||||
|
||||
<td>
|
||||
<div class="row-name">
|
||||
{#if record.status === "draft"}
|
||||
<span class="status">DRAFT</span>
|
||||
{/if}
|
||||
{#if schema.type === "files"}
|
||||
<Preview {record} size="tiny" showFilename={true}/>
|
||||
{:else}
|
||||
<a
|
||||
href="{channel.lucentUrl}/records/{record.id}"
|
||||
|
||||
>
|
||||
{previewTitle(channel.schemas, record, graph)}
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
</td>
|
||||
<td><a
|
||||
href="{channel.lucentUrl}/content/{schema.name}">{schema.label}</a
|
||||
>
|
||||
</td>
|
||||
|
||||
|
||||
<td>
|
||||
<div style="display: flex;gap: 14px">
|
||||
<Avatar name={usernameById(users, record._sys.updatedBy)} side={24}/>
|
||||
<div class="ms-2">
|
||||
{frieldlyUpdatedAt}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
@@ -1,39 +0,0 @@
|
||||
<script>
|
||||
import NavbarMenu from "./NavbarMenu.svelte";
|
||||
import {getContext} from "svelte";
|
||||
|
||||
export let schema;
|
||||
const channel = getContext("channel");
|
||||
const readableSchemas = getContext("readableSchemas");
|
||||
|
||||
const fileSchemas = readableSchemas.filter((sc) => sc.type === "files");
|
||||
const otherSchemas = readableSchemas.filter((sc) => !sc.isEntry && sc.type === "collection");
|
||||
|
||||
</script>
|
||||
<div class="sidebar-top">
|
||||
<a class="logo" href="{channel.lucentUrl}">{channel.name}</a>
|
||||
<a class="nav-item" href="{channel.lucentUrl}/profile">
|
||||
</a>
|
||||
</div>
|
||||
<div class="sidebar">
|
||||
|
||||
|
||||
<NavbarMenu
|
||||
title="Content"
|
||||
schemas={ readableSchemas.filter((sc) => sc.isEntry)}
|
||||
schema={schema}
|
||||
expanded={true}
|
||||
/>
|
||||
|
||||
<NavbarMenu
|
||||
title="Files"
|
||||
schemas={ fileSchemas}
|
||||
schema={schema}
|
||||
/>
|
||||
|
||||
<NavbarMenu
|
||||
title="Other"
|
||||
schemas={ otherSchemas}
|
||||
schema={schema}
|
||||
/>
|
||||
</div>
|
||||
@@ -1,14 +1,13 @@
|
||||
<script>
|
||||
import {afterUpdate, getContext, onMount} from "svelte";
|
||||
import {isEqual} from "lodash";
|
||||
import { afterUpdate, getContext, onMount } from "svelte";
|
||||
import axios from "axios";
|
||||
import EditHeader from "./header/EditHeader.svelte"
|
||||
import FilePreview from "./FilePreview.svelte"
|
||||
import ContentTabs from "./header/ContentTabs.svelte"
|
||||
import FormField from "./FormField.svelte"
|
||||
import Graph from "./Graph.svelte"
|
||||
import Info from "./Info.svelte"
|
||||
import ErrorAlert from "../common/ErrorAlert.svelte"
|
||||
import EditHeader from "./header/EditHeader.svelte";
|
||||
import FilePreview from "./FilePreview.svelte";
|
||||
import ContentTabs from "./header/ContentTabs.svelte";
|
||||
import FormField from "./FormField.svelte";
|
||||
import Graph from "./Graph.svelte";
|
||||
import Info from "./Info.svelte";
|
||||
import ErrorAlert from "../common/ErrorAlert.svelte";
|
||||
import Title from "./header/Title.svelte";
|
||||
|
||||
const channel = getContext("channel");
|
||||
@@ -17,7 +16,7 @@
|
||||
export let record;
|
||||
export let graph = {
|
||||
records: [],
|
||||
edges: []
|
||||
edges: [],
|
||||
};
|
||||
// export let recordHistory;
|
||||
export let isCreateMode;
|
||||
@@ -29,14 +28,11 @@
|
||||
$: validationErrors = null;
|
||||
$: errorMessage = validationErrors
|
||||
? `Record submission failed. ${
|
||||
Object.entries(validationErrors).length
|
||||
} error(s)`
|
||||
Object.entries(validationErrors).length
|
||||
} error(s)`
|
||||
: null;
|
||||
|
||||
let activeFields = schema.fields.filter(
|
||||
(f) => f.name !== "id"
|
||||
);
|
||||
|
||||
let activeFields = schema.fields.filter((f) => f.name !== "id");
|
||||
|
||||
onMount(() => {
|
||||
setOriginalContent();
|
||||
@@ -104,7 +100,9 @@
|
||||
}
|
||||
|
||||
// remove trashed edges
|
||||
graph.edges = graph.edges?.filter((edge) => !edge._isTrashed && edge.source === record.id);
|
||||
graph.edges = graph.edges?.filter(
|
||||
(edge) => !edge._isTrashed && edge.source === record.id,
|
||||
);
|
||||
axios
|
||||
.post(channel.lucentUrl + "/records", {
|
||||
record: record,
|
||||
@@ -115,7 +113,8 @@
|
||||
console.log("SAVE: SAVED");
|
||||
|
||||
if (isCreateMode) {
|
||||
window.location = channel.lucentUrl + "/records/" + record.id;
|
||||
window.location =
|
||||
channel.lucentUrl + "/records/" + record.id;
|
||||
} else {
|
||||
record = response.data.records[0] ?? null;
|
||||
if (!record) {
|
||||
@@ -137,7 +136,7 @@
|
||||
errorMessage = error.response.data.error;
|
||||
} else {
|
||||
validationErrors = error.response.data.error;
|
||||
console.log(validationErrors)
|
||||
console.log(validationErrors);
|
||||
}
|
||||
}
|
||||
resolve(null);
|
||||
@@ -149,70 +148,61 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:beforeunload={beforeUnload}/>
|
||||
<svelte:window on:beforeunload={beforeUnload} />
|
||||
|
||||
<div class="record-edit">
|
||||
<div class="tools-header">
|
||||
<!-- <Manager managerRecords={recordHistory} {graph}/>-->
|
||||
<EditHeader {schema} bind:record {isCreateMode} bind:activeContentTab/>
|
||||
<EditHeader {schema} bind:record {isCreateMode} bind:activeContentTab />
|
||||
{#if isCreateMode}
|
||||
<button
|
||||
class="button primary btn-spinner"
|
||||
on:click={save}
|
||||
>
|
||||
<span
|
||||
class="spinner-border spinner-border-sm"
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<button class="button primary btn-spinner" on:click={save}>
|
||||
<span
|
||||
class="spinner-border spinner-border-sm"
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Create
|
||||
</button>
|
||||
{:else if hasUnsavedData}
|
||||
<button
|
||||
type="button"
|
||||
class="button primary ms-2 btn btn-primary btn-spinner"
|
||||
on:click={save}
|
||||
type="button"
|
||||
class="button primary ms-2 btn btn-primary btn-spinner"
|
||||
on:click={save}
|
||||
>
|
||||
<span
|
||||
class="spinner-border spinner-border-sm"
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span
|
||||
class="spinner-border spinner-border-sm"
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Save
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
<Title {schema} {record} {isCreateMode}/>
|
||||
<Title {schema} {record} {isCreateMode} />
|
||||
|
||||
|
||||
<ErrorAlert message={errorMessage}/>
|
||||
<ErrorAlert message={errorMessage} />
|
||||
|
||||
<div class=" mt-4" style="margin-bottom:150px;position:relative;">
|
||||
<ContentTabs
|
||||
{schema}
|
||||
{isCreateMode}
|
||||
bind:active={activeContentTab}
|
||||
/>
|
||||
<ContentTabs {schema} {isCreateMode} bind:active={activeContentTab} />
|
||||
{#if !["_graph", "_info"].includes(activeContentTab)}
|
||||
<FilePreview {record} {schema}/>
|
||||
<FilePreview {record} {schema} />
|
||||
{#each activeFields as field (field.name)}
|
||||
{#if activeContentTab === field.group}
|
||||
<FormField
|
||||
bind:data={record.data}
|
||||
bind:graph={graph}
|
||||
{field}
|
||||
{schema}
|
||||
{record}
|
||||
{validationErrors}
|
||||
{isCreateMode}
|
||||
bind:data={record.data}
|
||||
bind:graph
|
||||
{field}
|
||||
{schema}
|
||||
{record}
|
||||
{validationErrors}
|
||||
{isCreateMode}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
{:else if activeContentTab === "_graph"}
|
||||
<Graph {graph} {record}/>
|
||||
<Graph {graph} {record} />
|
||||
{:else if activeContentTab === "_info"}
|
||||
<Info {record} {graph} {users} {schema}/>
|
||||
<Info {record} {graph} {users} {schema} />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
<script>
|
||||
import {friendlyDate} from "../../helpers";
|
||||
import { friendlyDate } from "../../helpers";
|
||||
import Avatar from "../account/Avatar.svelte";
|
||||
import {usernameById} from "../account/users";
|
||||
import {isEqual} from "lodash";
|
||||
import { usernameById } from "../account/users";
|
||||
import Icon from "../common/Icon.svelte";
|
||||
import RevisionCell from "./revisions/RevisionCell.svelte";
|
||||
import {getContext} from "svelte";
|
||||
import { getContext } from "svelte";
|
||||
import RevisionEdgeRow from "./revisions/RevisionEdgeRow.svelte";
|
||||
|
||||
const channel = getContext("channel");
|
||||
@@ -30,27 +29,27 @@
|
||||
});
|
||||
|
||||
function getEdgesByField(fieldsWithDiff, revision) {
|
||||
|
||||
edgeFieldsDiff = graph.edges.filter((e) => e.depth === 1).reduce((c, e) => {
|
||||
if (!c[e.field]) {
|
||||
c[e.field] = {
|
||||
record: [],
|
||||
revision: [],
|
||||
edgeFieldsDiff = graph.edges
|
||||
.filter((e) => e.depth === 1)
|
||||
.reduce((c, e) => {
|
||||
if (!c[e.field]) {
|
||||
c[e.field] = {
|
||||
record: [],
|
||||
revision: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
c[e.field]["record"].push(e)
|
||||
return c;
|
||||
}, {});
|
||||
|
||||
c[e.field]["record"].push(e);
|
||||
return c;
|
||||
}, {});
|
||||
|
||||
edgeFieldsDiff = revision._edges.reduce((c, e) => {
|
||||
if (!c[e.field]) {
|
||||
c[e.field] = {
|
||||
record: [],
|
||||
revision: [],
|
||||
}
|
||||
};
|
||||
}
|
||||
c[e.field]["revision"].push(e)
|
||||
c[e.field]["revision"].push(e);
|
||||
return c;
|
||||
}, edgeFieldsDiff);
|
||||
}
|
||||
@@ -62,7 +61,7 @@
|
||||
fieldsWithDiff = schema.fields.filter((f) => {
|
||||
return !isEqual(selectedRevision.data[f.name], record.data[f.name]);
|
||||
});
|
||||
getEdgesByField(fieldsWithDiff, revision)
|
||||
getEdgesByField(fieldsWithDiff, revision);
|
||||
revisionSection.scrollIntoView();
|
||||
}
|
||||
|
||||
@@ -71,7 +70,7 @@
|
||||
rollbackError = "";
|
||||
axios
|
||||
.post(
|
||||
`${channel.lucentUrl}/records/${record.id}/rollback/${selectedRevision._sys.version}`
|
||||
`${channel.lucentUrl}/records/${record.id}/rollback/${selectedRevision._sys.version}`,
|
||||
)
|
||||
.then((response) => {
|
||||
window.location.reload();
|
||||
@@ -84,7 +83,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="lx-card ">
|
||||
<div class="lx-card">
|
||||
<div class="row">
|
||||
<div class="col-8">
|
||||
<div>
|
||||
@@ -98,24 +97,22 @@
|
||||
<div>
|
||||
<span class="label text-end text-muted"> created </span>
|
||||
<Avatar
|
||||
name={usernameById(users, record._sys.createdBy)}
|
||||
side={24}
|
||||
name={usernameById(users, record._sys.createdBy)}
|
||||
side={24}
|
||||
/>
|
||||
{friendlyDate(record._sys.createdAt)}
|
||||
</div>
|
||||
<div>
|
||||
<span class="label text-end text-muted">updated </span>
|
||||
<span class="label text-end text-muted">updated </span>
|
||||
<Avatar
|
||||
name={usernameById(users, record._sys.updatedBy)}
|
||||
side={24}
|
||||
name={usernameById(users, record._sys.updatedBy)}
|
||||
side={24}
|
||||
/>
|
||||
{friendlyDate(record._sys.updatedAt)}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<span class="label d-block text-muted "
|
||||
>Rules for this schema
|
||||
</span>
|
||||
<span class="label d-block text-muted">Rules for this schema </span>
|
||||
<small>
|
||||
Each record maintains the last {schema.revisions}
|
||||
versions
|
||||
@@ -125,33 +122,31 @@
|
||||
</div>
|
||||
<div class="revisions">
|
||||
{#if schema.revisions > 0}
|
||||
<div class="header-small mb-3">Revisions</div>
|
||||
<div class="header-small mb-3">Revisions</div>
|
||||
{#each revisions as revision}
|
||||
{#if revision._sys.version !== record._sys.version}
|
||||
<div
|
||||
class="revision"
|
||||
class:active={revision._sys.version ===
|
||||
class="revision"
|
||||
class:active={revision._sys.version ===
|
||||
selectedRevision?._sys.version}
|
||||
>
|
||||
|
||||
<div class="version">
|
||||
<span>version {revision._sys.version}</span>
|
||||
<Avatar
|
||||
name={usernameById(users, revision._sys.updatedBy)}
|
||||
side={24}
|
||||
name={usernameById(users, revision._sys.updatedBy)}
|
||||
side={24}
|
||||
/>
|
||||
{friendlyDate(revision._sys.updatedAt)}
|
||||
</div>
|
||||
|
||||
<div class="col-3 text-center">
|
||||
<button
|
||||
disabled={revision._sys.version ===
|
||||
disabled={revision._sys.version ===
|
||||
selectedRevision?._sys.version}
|
||||
class="button"
|
||||
on:click={(e) => compare(e, revision)}
|
||||
>Compare
|
||||
</button
|
||||
>
|
||||
class="button"
|
||||
on:click={(e) => compare(e, revision)}
|
||||
>Compare
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -169,15 +164,13 @@
|
||||
<p class="text-center fw-bold mb-3 mt-5">
|
||||
If you choose to rollback to this revision
|
||||
</p>
|
||||
<button
|
||||
on:click={rollback}
|
||||
class="button"
|
||||
>
|
||||
<button on:click={rollback} class="button">
|
||||
Rollback to version {selectedRevision._sys.version}
|
||||
</button>
|
||||
|
||||
{#if rollbackError}
|
||||
<span class="d-block text-danger mt-3">{rollbackError}</span>
|
||||
<span class="d-block text-danger mt-3">{rollbackError}</span
|
||||
>
|
||||
{/if}
|
||||
<div class="mt-3">
|
||||
{#each fieldsWithDiff as field}
|
||||
@@ -188,31 +181,28 @@
|
||||
<!-- <div class="d-block" style="width:200px;">
|
||||
{field.label}
|
||||
</div> -->
|
||||
<div
|
||||
class="revision-field"
|
||||
style="overflow:hidden"
|
||||
>
|
||||
<div class="revision-field" style="overflow:hidden">
|
||||
<div class="compare-left">
|
||||
<RevisionCell
|
||||
{field}
|
||||
side={record.data[field.name]}
|
||||
colorClass="text-danger"
|
||||
{field}
|
||||
side={record.data[field.name]}
|
||||
colorClass="text-danger"
|
||||
/>
|
||||
</div>
|
||||
<div class="compare-center">
|
||||
<span class="me-1">{field.label}</span>
|
||||
<Icon
|
||||
icon="angle-right"
|
||||
width="12"
|
||||
height="12"
|
||||
icon="angle-right"
|
||||
width="12"
|
||||
height="12"
|
||||
/>
|
||||
</div>
|
||||
<div class="compare-right">
|
||||
<RevisionCell
|
||||
edges={selectedRevision._edges}
|
||||
{field}
|
||||
side={selectedRevision.data[field.name]}
|
||||
colorClass="text-success"
|
||||
edges={selectedRevision._edges}
|
||||
{field}
|
||||
side={selectedRevision.data[field.name]}
|
||||
colorClass="text-success"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -226,22 +216,16 @@
|
||||
{/if}
|
||||
|
||||
<div class="mt-3">
|
||||
<p class="text-center fw-bold mb-3 mt-5">
|
||||
Record References
|
||||
</p>
|
||||
<p class="text-center fw-bold mb-3 mt-5">Record References</p>
|
||||
{#each Object.entries(edgeFieldsDiff) as [field, edges]}
|
||||
<div
|
||||
class="revision-references"
|
||||
style="overflow:hidden"
|
||||
>
|
||||
<div class="revision-references" style="overflow:hidden">
|
||||
<div class="reference-field">
|
||||
{field}:
|
||||
</div>
|
||||
<div class="reference-compare">
|
||||
|
||||
<p class="">Record</p>
|
||||
{#each edges.record as edge}
|
||||
<RevisionEdgeRow {edge}/>
|
||||
<RevisionEdgeRow {edge} />
|
||||
{:else}
|
||||
<p>No references</p>
|
||||
{/each}
|
||||
@@ -249,7 +233,7 @@
|
||||
<div class="reference-compare">
|
||||
<p class="text-success">Revision</p>
|
||||
{#each edges.revision as edge}
|
||||
<RevisionEdgeRow {edge}/>
|
||||
<RevisionEdgeRow {edge} />
|
||||
{:else}
|
||||
<p>No references</p>
|
||||
{/each}
|
||||
@@ -258,7 +242,5 @@
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
<script>
|
||||
import {afterUpdate, createEventDispatcher, getContext, onMount} from "svelte";
|
||||
import {
|
||||
afterUpdate,
|
||||
createEventDispatcher,
|
||||
getContext,
|
||||
onMount,
|
||||
} from "svelte";
|
||||
|
||||
import {isEqual} from "lodash";
|
||||
import FormField from "./FormField.svelte";
|
||||
import FilePreview from "./FilePreview.svelte";
|
||||
import ContentTabs from "./header/ContentTabs.svelte";
|
||||
@@ -16,7 +20,7 @@
|
||||
export let record;
|
||||
export let graph = {
|
||||
records: [],
|
||||
edges: []
|
||||
edges: [],
|
||||
};
|
||||
export let isCreateMode;
|
||||
let originalContent;
|
||||
@@ -25,13 +29,11 @@
|
||||
$: validationErrors = null;
|
||||
$: errorMessage = validationErrors
|
||||
? `Record submission failed. ${
|
||||
Object.entries(validationErrors).length
|
||||
} error(s)`
|
||||
Object.entries(validationErrors).length
|
||||
} error(s)`
|
||||
: null;
|
||||
|
||||
let activeFields = schema.fields.filter(
|
||||
(f) => f.name !== "id"
|
||||
);
|
||||
let activeFields = schema.fields.filter((f) => f.name !== "id");
|
||||
|
||||
let tabname = "_default";
|
||||
let fieldToTabs = schema.fields.reduce((c, f) => {
|
||||
@@ -114,8 +116,10 @@
|
||||
return;
|
||||
}
|
||||
// remove trashed edges
|
||||
graph.edges = graph.edges?.filter((edge) => !edge._isTrashed && edge.source === record.id) ?? [];
|
||||
|
||||
graph.edges =
|
||||
graph.edges?.filter(
|
||||
(edge) => !edge._isTrashed && edge.source === record.id,
|
||||
) ?? [];
|
||||
|
||||
axios
|
||||
.post(channel.lucentUrl + "/records", {
|
||||
@@ -150,64 +154,55 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:beforeunload={beforeUnload}/>
|
||||
<svelte:window on:beforeunload={beforeUnload} />
|
||||
|
||||
<div class="inline-edit record-edit">
|
||||
<div class="tools-header">
|
||||
<EditHeader {schema} bind:record {isCreateMode} bind:activeContentTab/>
|
||||
<EditHeader {schema} bind:record {isCreateMode} bind:activeContentTab />
|
||||
{#if isCreateMode}
|
||||
<button
|
||||
class="button primary btn-spinner"
|
||||
on:click={save}
|
||||
>
|
||||
<span
|
||||
class="spinner-border spinner-border-sm"
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<button class="button primary btn-spinner" on:click={save}>
|
||||
<span
|
||||
class="spinner-border spinner-border-sm"
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Create
|
||||
</button>
|
||||
{:else if hasUnsavedData}
|
||||
<button
|
||||
type="button"
|
||||
class="button primary ms-2 btn btn-primary btn-spinner"
|
||||
on:click={save}
|
||||
type="button"
|
||||
class="button primary ms-2 btn btn-primary btn-spinner"
|
||||
on:click={save}
|
||||
>
|
||||
<span
|
||||
class="spinner-border spinner-border-sm"
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span
|
||||
class="spinner-border spinner-border-sm"
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Save
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
<Title {schema} {record} {isCreateMode}/>
|
||||
<ErrorAlert message={errorMessage}/>
|
||||
<Title {schema} {record} {isCreateMode} />
|
||||
<ErrorAlert message={errorMessage} />
|
||||
|
||||
<div class=" mt-4" style="margin-bottom:150px;position:relative;">
|
||||
<ContentTabs
|
||||
{schema}
|
||||
{isCreateMode}
|
||||
bind:active={activeContentTab}
|
||||
/>
|
||||
<FilePreview {record} {schema}/>
|
||||
<ContentTabs {schema} {isCreateMode} bind:active={activeContentTab} />
|
||||
<FilePreview {record} {schema} />
|
||||
<!-- <fieldset disabled="disabled"> -->
|
||||
{#each activeFields as field (field.name)}
|
||||
{#if activeContentTab === field.group}
|
||||
<FormField
|
||||
bind:data={record.data}
|
||||
bind:graph={graph}
|
||||
{field}
|
||||
{schema}
|
||||
{record}
|
||||
{validationErrors}
|
||||
{isCreateMode}
|
||||
bind:data={record.data}
|
||||
bind:graph
|
||||
{field}
|
||||
{schema}
|
||||
{record}
|
||||
{validationErrors}
|
||||
{isCreateMode}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
<!-- </fieldset> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
<script>
|
||||
import {getContext} from "svelte";
|
||||
import {debounce} from "lodash";
|
||||
import {previewTitle} from "../Preview";
|
||||
import {getErrorMessage} from "./errorMessage";
|
||||
import {insertEdges} from "./reference.js";
|
||||
import { getContext } from "svelte";
|
||||
import { previewTitle } from "../Preview";
|
||||
import { getErrorMessage } from "./errorMessage";
|
||||
import { insertEdges } from "./reference.js";
|
||||
import Icon from "../../common/Icon.svelte";
|
||||
|
||||
const channel = getContext("channel");
|
||||
@@ -15,20 +14,24 @@
|
||||
export let validationErrors;
|
||||
$: errorMessage = getErrorMessage(validationErrors, field.name);
|
||||
|
||||
$: references = graph.edges
|
||||
.filter((edge) => edge.field === field.name)
|
||||
.map((edge) => {
|
||||
return graph.records.find((increc) => increc.id == edge.target && record.id == edge.source);
|
||||
}).filter((rec) => (rec?.id ? true : false)) ?? [];
|
||||
|
||||
let search = ""
|
||||
$: searchOptions = []
|
||||
$: references =
|
||||
graph.edges
|
||||
.filter((edge) => edge.field === field.name)
|
||||
.map((edge) => {
|
||||
return graph.records.find(
|
||||
(increc) =>
|
||||
increc.id == edge.target && record.id == edge.source,
|
||||
);
|
||||
})
|
||||
.filter((rec) => (rec?.id ? true : false)) ?? [];
|
||||
|
||||
let search = "";
|
||||
$: searchOptions = [];
|
||||
|
||||
function removeReference(e, recordId) {
|
||||
e.preventDefault();
|
||||
graph.edges = graph.edges.filter(
|
||||
(edge) => !(edge.target === recordId && edge.field === field.name)
|
||||
(edge) => !(edge.target === recordId && edge.field === field.name),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -41,14 +44,14 @@
|
||||
schema: field.collections[0],
|
||||
status: "published",
|
||||
data: {
|
||||
[field.searchField]: newValue
|
||||
}
|
||||
[field.searchField]: newValue,
|
||||
},
|
||||
},
|
||||
})
|
||||
.then((response) => {
|
||||
searchOptions = [];
|
||||
insert(e, response.data.records[0]);
|
||||
console.log(response)
|
||||
console.log(response);
|
||||
})
|
||||
.catch((error) => {
|
||||
searchOptions = [];
|
||||
@@ -58,10 +61,16 @@
|
||||
|
||||
function insert(e, insertRecord) {
|
||||
e.preventDefault();
|
||||
graph = insertEdges(graph, record, [insertRecord], field.name, e.detail.action);
|
||||
search = ""
|
||||
searchEl.focus()
|
||||
searchEl.blur()
|
||||
graph = insertEdges(
|
||||
graph,
|
||||
record,
|
||||
[insertRecord],
|
||||
field.name,
|
||||
e.detail.action,
|
||||
);
|
||||
search = "";
|
||||
searchEl.focus();
|
||||
searchEl.blur();
|
||||
}
|
||||
|
||||
const updateResults = debounce((e) => {
|
||||
@@ -82,9 +91,8 @@
|
||||
console.log(error);
|
||||
});
|
||||
}, 500);
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<div class="reference-tags">
|
||||
{#if errorMessage}
|
||||
<div class="invalid-feedback d-block mb-3">
|
||||
@@ -93,44 +101,39 @@
|
||||
{/if}
|
||||
|
||||
<input
|
||||
type="search"
|
||||
bind:this={searchEl}
|
||||
{id}
|
||||
on:keyup={updateResults}
|
||||
class:is-invalid={errorMessage}
|
||||
bind:value={search}
|
||||
placeholder={"Search for "+field.label}
|
||||
autocomplete="off"
|
||||
type="search"
|
||||
bind:this={searchEl}
|
||||
{id}
|
||||
on:keyup={updateResults}
|
||||
class:is-invalid={errorMessage}
|
||||
bind:value={search}
|
||||
placeholder={"Search for " + field.label}
|
||||
autocomplete="off"
|
||||
/>
|
||||
|
||||
<div class="reference-tags-results">
|
||||
|
||||
{#if searchOptions}
|
||||
{#each searchOptions as option (option.id)}
|
||||
<div
|
||||
class="reference-tags-option"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
on:click={(e) => insert(e, option)}
|
||||
on:keypress={(e) => insert(e, option)}
|
||||
>
|
||||
{previewTitle(channel.schemas, option ,graph)}
|
||||
</div>
|
||||
|
||||
{:else}
|
||||
<div
|
||||
class="start-typing">
|
||||
Start typing...
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
{#if search }
|
||||
<div
|
||||
class="reference-tags-option"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
on:click={(e) => saveNew(e,search)}
|
||||
on:keypress={(e) => saveNew(e,search)}
|
||||
on:click={(e) => insert(e, option)}
|
||||
on:keypress={(e) => insert(e, option)}
|
||||
>
|
||||
{previewTitle(channel.schemas, option, graph)}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="start-typing">Start typing...</div>
|
||||
{/each}
|
||||
{/if}
|
||||
{#if search}
|
||||
<div
|
||||
class="reference-tags-option"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
on:click={(e) => saveNew(e, search)}
|
||||
on:keypress={(e) => saveNew(e, search)}
|
||||
>
|
||||
Add "{search}"
|
||||
</div>
|
||||
@@ -142,26 +145,23 @@
|
||||
<div style="display: flex;align-items: center;gap: 4px">
|
||||
{#each references as record (record.id)}
|
||||
<span class="reference-tags-selected-value">
|
||||
<a
|
||||
class="record-title"
|
||||
href="{channel.lucentUrl}/records/{record.id}"
|
||||
>
|
||||
{previewTitle(channel.schemas, record)}
|
||||
</a>
|
||||
<a
|
||||
class="record-title"
|
||||
href="{channel.lucentUrl}/records/{record.id}"
|
||||
>
|
||||
{previewTitle(channel.schemas, record)}
|
||||
</a>
|
||||
|
||||
<button
|
||||
on:click|preventDefault={(e) => removeReference(e, record.id)}
|
||||
type="button"
|
||||
class="button-text"
|
||||
aria-label="Close"
|
||||
on:click|preventDefault={(e) =>
|
||||
removeReference(e, record.id)}
|
||||
type="button"
|
||||
class="button-text"
|
||||
aria-label="Close"
|
||||
>
|
||||
<Icon width={12} height={12} icon="close"></Icon>
|
||||
</button>
|
||||
|
||||
<Icon width={12} height={12} icon="close"></Icon>
|
||||
</button>
|
||||
</span>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script>
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { getContext } from "svelte";
|
||||
import Icon from "../../common/Icon.svelte";
|
||||
import { getErrorMessage } from "./errorMessage";
|
||||
@@ -14,12 +13,11 @@
|
||||
|
||||
function generateId(e) {
|
||||
e.preventDefault();
|
||||
value = uuidv4();
|
||||
value = randomUUID();
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="mb-0">
|
||||
|
||||
<div class="d-flex justify-content-between">
|
||||
<input
|
||||
type="text"
|
||||
@@ -31,13 +29,13 @@
|
||||
{readonly}
|
||||
/>
|
||||
{#if !readonly}
|
||||
<button
|
||||
class="btn btn-primary ms-2"
|
||||
title="Generate a new UUIDv4"
|
||||
on:click={generateId}
|
||||
>
|
||||
<Icon icon="dice" />
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-primary ms-2"
|
||||
title="Generate a new UUIDv4"
|
||||
on:click={generateId}
|
||||
>
|
||||
<Icon icon="dice" />
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
<script>
|
||||
import { uniqueId } from "lodash";
|
||||
import { getContext } from "svelte";
|
||||
const channelurl = getContext("channelurl");
|
||||
export let field;
|
||||
export let value;
|
||||
export let schema;
|
||||
let id = uniqueId();
|
||||
let id = randomUUID();
|
||||
</script>
|
||||
|
||||
<div class="mb-0">
|
||||
@@ -25,6 +24,6 @@
|
||||
placeholder="https://www.example.com"
|
||||
/>
|
||||
{#if field.help}
|
||||
<small class=" text-primary opacity-50">{field.help}</small>
|
||||
<small class=" text-primary opacity-50">{field.help}</small>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -1,24 +1,31 @@
|
||||
import {uniqBy} from "lodash";
|
||||
export function insertEdges(
|
||||
graph,
|
||||
sourceRecord,
|
||||
targetRecords,
|
||||
fieldName,
|
||||
action = "",
|
||||
) {
|
||||
let newEdges = targetRecords.map((r) => {
|
||||
return {
|
||||
target: r.id,
|
||||
source: sourceRecord.id,
|
||||
sourceSchema: sourceRecord.schema,
|
||||
targetSchema: r.schema,
|
||||
field: fieldName,
|
||||
depth: 1,
|
||||
rank: "",
|
||||
};
|
||||
});
|
||||
|
||||
export function insertEdges(graph, sourceRecord, targetRecords, fieldName, action = "") {
|
||||
let newEdges = targetRecords.map((r) => {
|
||||
return {
|
||||
target: r.id,
|
||||
source: sourceRecord.id,
|
||||
sourceSchema: sourceRecord.schema,
|
||||
targetSchema: r.schema,
|
||||
field: fieldName,
|
||||
depth: 1,
|
||||
rank: ""
|
||||
};
|
||||
});
|
||||
let replacedEdges = graph.edges;
|
||||
if (action === "replace") {
|
||||
replacedEdges = replacedEdges.filter((edge) => edge.field !== field.name);
|
||||
}
|
||||
|
||||
let replacedEdges = graph.edges;
|
||||
if (action === "replace") {
|
||||
replacedEdges = replacedEdges.filter((edge) => edge.field !== field.name);
|
||||
}
|
||||
|
||||
graph.records = uniqBy([...graph.records, ...targetRecords], (r) => r.id);
|
||||
graph.edges = uniqBy([...replacedEdges, ...newEdges], (edge) => edge.source + edge.target + edge.field + edge.depth);
|
||||
return graph;
|
||||
graph.records = uniqBy([...graph.records, ...targetRecords], (r) => r.id);
|
||||
graph.edges = uniqBy(
|
||||
[...replacedEdges, ...newEdges],
|
||||
(edge) => edge.source + edge.target + edge.field + edge.depth,
|
||||
);
|
||||
return graph;
|
||||
}
|
||||
|
||||
Generated
+4611
-4376
File diff suppressed because it is too large
Load Diff
+28
-32
@@ -1,34 +1,30 @@
|
||||
{
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@codemirror/commands": "^6.6.0",
|
||||
"@codemirror/lang-json": "^6.0.1",
|
||||
"@codemirror/lang-markdown": "^6.2.5",
|
||||
"@codemirror/state": "^6.4.1",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.1.1",
|
||||
"axios": "^1.7.4",
|
||||
"codemirror": "^6.0.1",
|
||||
"date-fns": "^3.6.0",
|
||||
"flatpickr": "^4.6.13",
|
||||
"fuse.js": "^7.0.0",
|
||||
"htmx.org": "^2.0.1",
|
||||
"install": "^0.13.0",
|
||||
"laravel-vite-plugin": "^1.0.5",
|
||||
"lodash": "^4.17.21",
|
||||
"mustache": "^4.2.0",
|
||||
"npm": "^10.8.2",
|
||||
"postcss": "8.4.31",
|
||||
"sass": "^1.77.8",
|
||||
"sortablejs": "^1.15.2",
|
||||
"svelte": "^4.2.18",
|
||||
"tinymce": "^6.8.4",
|
||||
"trix": "^2.1.5",
|
||||
"uuid": "^10.0.0",
|
||||
"vite": "5.2.6"
|
||||
}
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@codemirror/commands": "^6.6.0",
|
||||
"@codemirror/lang-json": "^6.0.1",
|
||||
"@codemirror/lang-markdown": "^6.2.5",
|
||||
"@codemirror/state": "^6.4.1",
|
||||
"@sveltejs/vite-plugin-svelte": "^6.2.3",
|
||||
"axios": "^1.7.4",
|
||||
"codemirror": "^6.0.1",
|
||||
"date-fns": "^3.6.0",
|
||||
"flatpickr": "^4.6.13",
|
||||
"fuse.js": "^7.0.0",
|
||||
"install": "^0.13.0",
|
||||
"mustache": "^4.2.0",
|
||||
"npm": "^10.8.2",
|
||||
"postcss": "8.4.31",
|
||||
"sass": "^1.77.8",
|
||||
"sortablejs": "^1.15.2",
|
||||
"svelte": "^5.46.1",
|
||||
"tinymce": "^6.8.4",
|
||||
"trix": "^2.1.5",
|
||||
"vite": "^7.3.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,31 @@
|
||||
@extends('lucent::layouts.'.$layout)
|
||||
@section('title')
|
||||
{{ $title }}
|
||||
@endsection
|
||||
@section('content')
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
<title> {{ $title }} - Lucent Data Platform</title>
|
||||
@if(config("lucent.env") == "production")
|
||||
<!-- if production -->
|
||||
<link rel="stylesheet" href="{{url('vendor/lucent/dist/'.$manifest['main.js']["css"][0])}}"/>
|
||||
<script type="module" src="{{url('vendor/lucent/dist/'.$manifest['main.js']["file"])}}"></script>
|
||||
@else
|
||||
<!-- if development -->
|
||||
@php
|
||||
echo '<script type="module" crossorigin src="http://127.0.0.1:5173/@vite/client"></script>';
|
||||
@endphp
|
||||
<script type="module" crossorigin src="http://127.0.0.1:5173/main.js"></script>
|
||||
@endif
|
||||
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="{{url('favicon.ico')}}">
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{!! $svelte !!}
|
||||
@endsection
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
Generated
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "lucent-package",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
||||
@@ -10,9 +10,7 @@ use JsonSerializable;
|
||||
*/
|
||||
class ArrayContainer implements ArrayAccess, JsonSerializable
|
||||
{
|
||||
public function __construct(public array $data)
|
||||
{
|
||||
}
|
||||
public function __construct(public array $data) {}
|
||||
|
||||
public function get(string $key, mixed $default = null): mixed
|
||||
{
|
||||
|
||||
@@ -7,61 +7,39 @@ use Illuminate\Console\Command;
|
||||
use Lucent\Schema\Schema;
|
||||
use Lucent\Schema\SchemaService;
|
||||
use Lucent\Schema\Type;
|
||||
use Yosymfony\Toml\Toml;
|
||||
|
||||
class CompileSchemas extends Command
|
||||
{
|
||||
protected $signature = "lucent:schemas";
|
||||
|
||||
protected $signature = 'lucent:schemas';
|
||||
|
||||
protected $description = 'Compiles schemas';
|
||||
|
||||
protected $description = "Compiles schemas";
|
||||
|
||||
public function handle(SchemaService $schemaService)
|
||||
{
|
||||
|
||||
$configDir = base_path(config('lucent.schemas_path'));
|
||||
$configDir = base_path(config("lucent.schemas_path"));
|
||||
$schemasDirIterator = new DirectoryIterator($configDir);
|
||||
$schemas = [];
|
||||
|
||||
foreach ($schemasDirIterator as $file) {
|
||||
if ($file->getExtension() !== "json") {
|
||||
if ($file->getExtension() !== "toml") {
|
||||
continue;
|
||||
}
|
||||
$schemaData = Toml::ParseFile(
|
||||
$configDir . "/" . $file->getFilename(),
|
||||
);
|
||||
|
||||
$schemaJson = file_get_contents($configDir . "/" . $file->getFilename());
|
||||
$schema = json_decode($schemaJson, true);
|
||||
if (empty($schema)) {
|
||||
$this->error("Invalid JSON " . $file->getFilename());
|
||||
return 0;
|
||||
}
|
||||
$schemas[] = $schema;
|
||||
|
||||
$schemas[] = $schemaData;
|
||||
}
|
||||
|
||||
$schemas = collect($schemas)->sortBy("label")->values();
|
||||
$roles = $schemas
|
||||
->map([$schemaService, 'fromArray'])
|
||||
->whereIn("type", [Type::COLLECTION, Type::FILES])
|
||||
->reduce(fn($carry, Schema $schema) => array_merge(
|
||||
$carry,
|
||||
$schema->read,
|
||||
$schema->write,
|
||||
config("lucent.canInvite") ?? [],
|
||||
config("lucent.canBuild") ?? [],
|
||||
), []);
|
||||
|
||||
$json = [
|
||||
"schemas" => $schemas->toArray(),
|
||||
"roles" => collect($roles)->push("admin")->push("removed")->unique()->values()->toArray()
|
||||
];
|
||||
|
||||
if (!file_exists(storage_path("lucent"))) {
|
||||
mkdir(storage_path("lucent"));
|
||||
}
|
||||
|
||||
file_put_contents(schemas_path(), json_encode($json));
|
||||
file_put_contents(schemas_path(), json_encode($schemas));
|
||||
|
||||
$this->info("Lucent Schemas were updated");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace Lucent\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class DeveloperMode extends Command
|
||||
{
|
||||
protected $signature = "lucent:developer {enable} {path}";
|
||||
protected $description = "Change composer.json to enable or disable the developer repository";
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
$enable = $this->argument("enable") === "enable";
|
||||
$path = $this->argument("path");
|
||||
|
||||
if ($enable) {
|
||||
$this->enable($path);
|
||||
} else {
|
||||
$this->disable($path);
|
||||
}
|
||||
}
|
||||
|
||||
private function getComposerJson(): array
|
||||
{
|
||||
return json_decode(file_get_contents(base_path("composer.json")), true);
|
||||
}
|
||||
|
||||
private function replaceComposerJson(array $composerJson): void
|
||||
{
|
||||
file_put_contents(
|
||||
base_path("composer.json"),
|
||||
json_encode(
|
||||
$composerJson,
|
||||
JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
private function enable(string $path): void
|
||||
{
|
||||
$productionRepo = $this->productionRepository();
|
||||
$composerJson = $this->getComposerJson();
|
||||
$composerJson["repositories"] = array_filter(
|
||||
$composerJson["repositories"],
|
||||
fn($repo) => $repo["url"] !== $productionRepo["url"],
|
||||
);
|
||||
|
||||
$composerJson["repositories"][] = $this->devRepository($path);
|
||||
$this->replaceComposerJson($composerJson);
|
||||
}
|
||||
|
||||
private function disable(string $path): void
|
||||
{
|
||||
$devRepo = $this->devRepository($path);
|
||||
$composerJson = $this->getComposerJson();
|
||||
$composerJson["repositories"] = array_filter(
|
||||
$composerJson["repositories"],
|
||||
fn($repo) => $repo["url"] !== $devRepo["url"],
|
||||
);
|
||||
$composerJson["repositories"][] = $this->productionRepository();
|
||||
$this->replaceComposerJson($composerJson);
|
||||
}
|
||||
|
||||
private function productionRepository(): array
|
||||
{
|
||||
return [
|
||||
"type" => "vcs",
|
||||
"url" =>
|
||||
"ssh://git@code.radical-elements.com:2727/lucent/lucent-laravel.git",
|
||||
];
|
||||
}
|
||||
|
||||
private function devRepository(string $localPath): array
|
||||
{
|
||||
return [
|
||||
"type" => "path",
|
||||
"url" => $localPath,
|
||||
"options" => [
|
||||
"symlink" => true,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php namespace Lucent\Core\Schema\Data;
|
||||
|
||||
class Field
|
||||
{
|
||||
public function __construct(
|
||||
public string $id,
|
||||
public string $label,
|
||||
public string $type,
|
||||
) {}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php namespace Lucent\Core\Schema\Data;
|
||||
|
||||
class Schema
|
||||
{
|
||||
/**
|
||||
* @param string $id
|
||||
* @param string $label
|
||||
* @param Field[] $fields
|
||||
*/
|
||||
public function __construct(
|
||||
public string $id,
|
||||
public string $label,
|
||||
public array $fields,
|
||||
) {}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php namespace Lucent\Core\Schema;
|
||||
|
||||
class SchemaModule
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
// Constructor logic here
|
||||
}
|
||||
}
|
||||
@@ -14,21 +14,14 @@ use function Lucent\Response\ok;
|
||||
class HomeController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly Svelte $svelte,
|
||||
private readonly Svelte $svelte,
|
||||
private readonly AccountService $accountService,
|
||||
private readonly Query $query,
|
||||
)
|
||||
{
|
||||
}
|
||||
private readonly Query $query,
|
||||
) {}
|
||||
|
||||
public function home(): View
|
||||
{
|
||||
|
||||
return $this->svelte->render(
|
||||
layout: "channel",
|
||||
view: "homeIndex",
|
||||
title: "Records",
|
||||
);
|
||||
return $this->svelte->render(view: "homeIndex", title: "Records");
|
||||
}
|
||||
|
||||
public function records(Request $request): Response
|
||||
@@ -38,10 +31,13 @@ class HomeController extends Controller
|
||||
|
||||
$sort = data_get($urlParams, "sort") ?? "-_sys.updatedAt";
|
||||
$filter = data_get($urlParams, "filter") ?? [];
|
||||
$arguments = array_merge([
|
||||
"schema_in" => $this->accountService->currentReadableSchemas(),
|
||||
"status_in" => ["draft", "published"]
|
||||
], $filter);
|
||||
$arguments = array_merge(
|
||||
[
|
||||
"schema_in" => $this->accountService->currentReadableSchemas(),
|
||||
"status_in" => ["draft", "published"],
|
||||
],
|
||||
$filter,
|
||||
);
|
||||
|
||||
$limit = 20;
|
||||
|
||||
|
||||
@@ -1,18 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Lucent\Http\Controller;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Lucent\Account\AccountService;
|
||||
use Lucent\Channel\ChannelService;
|
||||
use Lucent\LucentException;
|
||||
use Lucent\Setup\Data\SetupStep;
|
||||
use Lucent\Setup\Data\SetupStepStatus;
|
||||
use Lucent\Setup\Setup;
|
||||
use Lucent\Setup\Step\ComposerStep;
|
||||
use Lucent\Setup\Step\DatabaseSetupStep;
|
||||
use Lucent\Setup\Step\IStep;
|
||||
@@ -21,52 +16,54 @@ use Lucent\Setup\Step\LucentConfigStep;
|
||||
use Lucent\Setup\Step\StorageLinkSetupStep;
|
||||
use Lucent\Setup\Step\StorageSetupStep;
|
||||
use Lucent\Svelte\Svelte;
|
||||
use function Lucent\Response\fail;
|
||||
use function Lucent\Response\ok;
|
||||
|
||||
|
||||
class SetupController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly AccountService $accountService,
|
||||
private readonly ChannelService $channelService,
|
||||
private readonly Svelte $svelte,
|
||||
)
|
||||
{
|
||||
|
||||
}
|
||||
private readonly Svelte $svelte,
|
||||
) {}
|
||||
|
||||
public function setup(Request $request): View|RedirectResponse
|
||||
{
|
||||
$steps = array_reduce(
|
||||
[
|
||||
new ComposerStep(),
|
||||
new LucentConfigStep(),
|
||||
new LaravelEnvStep(),
|
||||
new StorageSetupStep(),
|
||||
new StorageLinkSetupStep(),
|
||||
new DatabaseSetupStep(),
|
||||
],
|
||||
fn(array $carry, IStep $setupStep) => array_merge($carry, [
|
||||
$setupStep(),
|
||||
]),
|
||||
[],
|
||||
);
|
||||
$allSuccess = array_reduce(
|
||||
$steps,
|
||||
fn(bool $carry, SetupStep $step) => !$carry
|
||||
? false
|
||||
: $step->status === SetupStepStatus::SUCCESS,
|
||||
true,
|
||||
);
|
||||
|
||||
|
||||
$steps = array_reduce([
|
||||
new ComposerStep,
|
||||
new LucentConfigStep,
|
||||
new LaravelEnvStep,
|
||||
new StorageSetupStep,
|
||||
new StorageLinkSetupStep,
|
||||
new DatabaseSetupStep,
|
||||
], fn(array $carry, IStep $setupStep) => array_merge($carry, [$setupStep()]), []);
|
||||
$allSuccess = array_reduce($steps, fn(bool $carry, SetupStep $step) => !$carry ? false : $step->status === SetupStepStatus::SUCCESS, true);
|
||||
|
||||
if($allSuccess){
|
||||
if ($allSuccess) {
|
||||
if ($this->accountService->countUsers() > 0) {
|
||||
return redirect($this->channelService->channel->lucentUrl . "/login");
|
||||
return redirect(
|
||||
$this->channelService->channel->lucentUrl . "/login",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->svelte->render(
|
||||
layout: "account",
|
||||
view: "setup",
|
||||
title: "Setup Lucent",
|
||||
data: [
|
||||
"steps" => $steps,
|
||||
"allSuccess" => $allSuccess,
|
||||
]
|
||||
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -15,9 +15,9 @@ use Lucent\Commands\LiveLink;
|
||||
use Lucent\Commands\RebuildThumbnails;
|
||||
use Lucent\Commands\RemoveOrphanEdges;
|
||||
use Lucent\Commands\SetupDatabase;
|
||||
use Lucent\Commands\DeveloperMode;
|
||||
use Lucent\Commands\UpgradeFiles122;
|
||||
use Lucent\File\FileService;
|
||||
use Lucent\File\ImageService;
|
||||
use Lucent\Query\DatabaseGraph\DatabaseGraph;
|
||||
use Lucent\Query\DatabaseGraph\PgsqlDatabaseGraph;
|
||||
use Lucent\Query\DatabaseGraph\SqliteDatabaseGraph;
|
||||
@@ -34,11 +34,9 @@ class LucentServiceProvider extends ServiceProvider
|
||||
});
|
||||
|
||||
$this->app->bind(ImageManager::class, function () {
|
||||
return new ImageManager(['driver' => 'imagick']);
|
||||
return new ImageManager(["driver" => "imagick"]);
|
||||
});
|
||||
|
||||
|
||||
|
||||
$this->app->bind(DatabaseGraph::class, function () {
|
||||
$dbConnection = config("lucent.database");
|
||||
return match (config("database.connections.$dbConnection.driver")) {
|
||||
@@ -46,9 +44,6 @@ class LucentServiceProvider extends ServiceProvider
|
||||
"pgsql" => new PgsqlDatabaseGraph(),
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,20 +51,29 @@ class LucentServiceProvider extends ServiceProvider
|
||||
*/
|
||||
public function boot(Router $router): void
|
||||
{
|
||||
|
||||
$manifestPath = public_path('vendor/lucent/dist/manifest.json');
|
||||
$manifestPath = public_path("vendor/lucent/dist/manifest.json");
|
||||
$manifest = null;
|
||||
if (file_exists($manifestPath)) {
|
||||
$manifest = json_decode(file_get_contents(public_path('vendor/lucent/dist/manifest.json')), true);
|
||||
$manifest = json_decode(
|
||||
file_get_contents(
|
||||
public_path("vendor/lucent/dist/manifest.json"),
|
||||
),
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
$router->aliasMiddleware(
|
||||
"lucent.auth",
|
||||
\Lucent\Http\Middleware\AuthMiddleware::class,
|
||||
);
|
||||
$router->aliasMiddleware(
|
||||
"lucent.guest",
|
||||
\Lucent\Http\Middleware\GuestMiddleware::class,
|
||||
);
|
||||
|
||||
$router->aliasMiddleware('lucent.auth', \Lucent\Http\Middleware\AuthMiddleware::class);
|
||||
$router->aliasMiddleware('lucent.guest', \Lucent\Http\Middleware\GuestMiddleware::class);
|
||||
|
||||
$this->loadViewsFrom(__DIR__ . '/../front/views', 'lucent');
|
||||
$this->loadRoutesFrom(__DIR__ . '/Http/web.php');
|
||||
$this->loadRoutesFrom(__DIR__ . '/Http/api.php');
|
||||
$this->loadViewsFrom(__DIR__ . "/../front/views", "lucent");
|
||||
$this->loadRoutesFrom(__DIR__ . "/Http/web.php");
|
||||
$this->loadRoutesFrom(__DIR__ . "/Http/api.php");
|
||||
|
||||
if ($this->app->runningInConsole()) {
|
||||
$this->commands([
|
||||
@@ -81,22 +85,32 @@ class LucentServiceProvider extends ServiceProvider
|
||||
GenerateCollectionSchema::class,
|
||||
GenerateFileSchema::class,
|
||||
UpgradeFiles122::class,
|
||||
DeveloperMode::class,
|
||||
]);
|
||||
}
|
||||
|
||||
View::share('manifest', $manifest);
|
||||
View::share('file', app()->make(FileService::class));
|
||||
Blade::anonymousComponentPath(__DIR__ . '../front/views/components', "lucent");
|
||||
|
||||
$this->publishes([
|
||||
__DIR__ . '/Config/main.php' => config_path('lucent.php'),
|
||||
],"lucent-config");
|
||||
|
||||
$this->publishes([
|
||||
__DIR__ . '/../front/dist' => public_path('vendor/lucent/dist'),
|
||||
__DIR__ . '/../front/public' => public_path('vendor/lucent/public'),
|
||||
], 'lucent');
|
||||
View::share("manifest", $manifest);
|
||||
View::share("file", app()->make(FileService::class));
|
||||
Blade::anonymousComponentPath(
|
||||
__DIR__ . "../front/views/components",
|
||||
"lucent",
|
||||
);
|
||||
|
||||
$this->publishes(
|
||||
[
|
||||
__DIR__ . "/Config/main.php" => config_path("lucent.php"),
|
||||
],
|
||||
"lucent-config",
|
||||
);
|
||||
|
||||
$this->publishes(
|
||||
[
|
||||
__DIR__ . "/../front/dist" => public_path("vendor/lucent/dist"),
|
||||
__DIR__ . "/../front/public" => public_path(
|
||||
"vendor/lucent/public",
|
||||
),
|
||||
],
|
||||
"lucent",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+23
-22
@@ -7,42 +7,43 @@ use Illuminate\Contracts\View\View;
|
||||
use Lucent\Account\AccountService;
|
||||
use Lucent\Channel\ChannelService;
|
||||
|
||||
class Svelte
|
||||
class Svelte
|
||||
{
|
||||
public function __construct(
|
||||
public ChannelService $channelService,
|
||||
public AccountService $accountService
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
function render(string $layout, string $view, string $title = "", mixed $data = []): View|Factory
|
||||
{
|
||||
public AccountService $accountService,
|
||||
) {}
|
||||
|
||||
function render(
|
||||
string $view,
|
||||
string $title = "",
|
||||
mixed $data = [],
|
||||
): View|Factory {
|
||||
$context = [];
|
||||
$context["user"] = session('user');
|
||||
$context["user"] = session("user");
|
||||
$context["view"] = $view;
|
||||
$context["layout"] = $layout;
|
||||
$context["title"] = $title;
|
||||
$context["data"] = $data;
|
||||
$context["channel"] = $this->channelService->channel;
|
||||
$context["readableSchemas"] = $this->accountService->currentReadableSchemas();
|
||||
$context[
|
||||
"readableSchemas"
|
||||
] = $this->accountService->currentReadableSchemas();
|
||||
$json = json_encode($context);
|
||||
|
||||
$divTag = sprintf('<div class="lucent-component" data-layout="%s"></div>', $layout);
|
||||
$jsonTag = sprintf('<script type="application/json" id="json-%s">%s</script>', $layout, $json);
|
||||
$divTag = '<div class="lucent-component"></div>';
|
||||
$jsonTag = sprintf(
|
||||
'<script type="application/json" id="json-data">%s</script>',
|
||||
$json,
|
||||
);
|
||||
|
||||
$svelte = $divTag . $jsonTag;
|
||||
|
||||
return view('lucent::svelte', [
|
||||
'svelte' => $svelte,
|
||||
'view' => $view,
|
||||
'data' => $data,
|
||||
'title' => $title,
|
||||
'layout' => $layout,
|
||||
'channel' => $this->channelService->channel,
|
||||
return view("lucent::svelte", [
|
||||
"svelte" => $svelte,
|
||||
"view" => $view,
|
||||
"data" => $data,
|
||||
"title" => $title,
|
||||
"channel" => $this->channelService->channel,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user