Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ecba85129d | |||
| b8c5f82e47 | |||
| 4389dba49d | |||
| fa388ea302 | |||
| 39e7a3aed4 | |||
| 5ed57838fc | |||
| 55db377abf | |||
| cebe24ea67 | |||
| b729df6923 | |||
| 843f560710 | |||
| 7574d67d80 | |||
| 19931cb4d1 | |||
| 6458c1e71d | |||
| 63232585ab | |||
| 6d15591601 | |||
| 32c8378020 | |||
| d0cd8228cc | |||
| c45a3847f8 | |||
| c0b3878674 | |||
| f868219981 | |||
| 8ac0567e66 | |||
| 02f8f5970a | |||
| 0cd4e08716 | |||
| cf3d621587 | |||
| 6fc0a65b6f | |||
| a73ee21568 | |||
| ff54bcc2ef | |||
| ab1517cc8f | |||
| 9f724a3243 | |||
| ae65ca47f6 | |||
| 74d2fcc4fa | |||
| 82174afdea | |||
| ffc39f078d | |||
| 7c4e19afbc | |||
| 7b10bfca1d | |||
| 0e5ac08641 | |||
| 1505aaa909 | |||
| d9e2c4954a | |||
| 97ad9de3d2 | |||
| 9e140be0ec | |||
| a737c2d571 | |||
| c43c29eb14 |
@@ -9,8 +9,8 @@ include_toc: true
|
|||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
||||||
- PHP 8.2
|
- PHP 8.3
|
||||||
- Laravel 10
|
- Laravel 11
|
||||||
- Postgres or Sqlite database
|
- Postgres or Sqlite database
|
||||||
- ImageMagick
|
- ImageMagick
|
||||||
|
|
||||||
@@ -82,7 +82,9 @@ return [
|
|||||||
### Database
|
### Database
|
||||||
|
|
||||||
The recommended database for small website is sqlite. But you can also use postresql
|
The recommended database for small website is sqlite. But you can also use postresql
|
||||||
Make sure to delete the existing migration scripts in your database/migrations folder.
|
|
||||||
|
> [!CAUTION]
|
||||||
|
> Make sure to delete the existing migration scripts in your database/migrations folder.
|
||||||
|
|
||||||
Then run:
|
Then run:
|
||||||
|
|
||||||
@@ -90,6 +92,26 @@ Then run:
|
|||||||
php artisan migrate
|
php artisan migrate
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### File Storage
|
||||||
|
|
||||||
|
You can use your local filesystem or s3 compatible storage. Lucent expects you to have a valid configuration inside ``config/filesystems.php``
|
||||||
|
|
||||||
|
example:
|
||||||
|
|
||||||
|
```php
|
||||||
|
return [
|
||||||
|
'disks' => [
|
||||||
|
'lucent' => [
|
||||||
|
'driver' => 'local',
|
||||||
|
'root' => storage_path('app/public'),
|
||||||
|
'url' => env('APP_URL').'/storage',
|
||||||
|
'visibility' => 'public',
|
||||||
|
'throw' => true,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
### First user
|
### First user
|
||||||
|
|
||||||
To create your first user, head to your localhost:8000/lucent
|
To create your first user, head to your localhost:8000/lucent
|
||||||
|
|||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
# Upgrade from 1.1.* to 1.2.0
|
||||||
|
|
||||||
|
## lucent.php config file
|
||||||
|
|
||||||
|
There is now an array of commands, accepting more than one.
|
||||||
|
|
||||||
|
from
|
||||||
|
```php
|
||||||
|
"generateCommand" => env("LUCENT_GENERATE_COMMAND", "generate:static"),
|
||||||
|
```
|
||||||
|
|
||||||
|
to
|
||||||
|
```php
|
||||||
|
"commands" => [
|
||||||
|
"generate:static" => "Build Website",
|
||||||
|
],
|
||||||
|
```
|
||||||
|
## config/filesystems.php
|
||||||
|
|
||||||
|
Lucent has its own filesystem.
|
||||||
|
|
||||||
|
You should now add:
|
||||||
|
|
||||||
|
```
|
||||||
|
'lucent' => [
|
||||||
|
'driver' => 'local',
|
||||||
|
'root' => storage_path('app/public'),
|
||||||
|
'url' => env('APP_URL').'/storage',
|
||||||
|
'visibility' => 'public',
|
||||||
|
'throw' => false,
|
||||||
|
],
|
||||||
|
```
|
||||||
+3
-3
@@ -8,14 +8,14 @@
|
|||||||
"ext-zip": "*",
|
"ext-zip": "*",
|
||||||
"ext-sqlite3": "*",
|
"ext-sqlite3": "*",
|
||||||
"ext-imagick": "*",
|
"ext-imagick": "*",
|
||||||
|
"ext-pdo": "*",
|
||||||
"php": "^8.3",
|
"php": "^8.3",
|
||||||
"guzzlehttp/guzzle": "^7.2",
|
"guzzlehttp/guzzle": "^7.2",
|
||||||
"intervention/image": "^2.7",
|
"intervention/image": "^2.7",
|
||||||
"phpoption/phpoption": "^1.9",
|
"phpoption/phpoption": "^1.9",
|
||||||
"spatie/image-optimizer": "^1.6",
|
"spatie/image-optimizer": "^1.6",
|
||||||
"staudenmeir/laravel-cte": "^1.0",
|
"staudenmeir/laravel-cte": "^1.0"
|
||||||
"ext-pdo": "*",
|
|
||||||
"mustache/mustache": "^2.14"
|
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpstan/phpstan": "^1.8",
|
"phpstan/phpstan": "^1.8",
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
"env" => env("LUCENT_ENV", "production"),
|
||||||
|
"schemas_path" => env("LUCENT_SCHEMAS_PATH", "resources/lucent/schemas"),
|
||||||
|
"database" => env('LUCENT_DB_CONNECTION', env('DB_CONNECTION', "sqlite")),
|
||||||
|
"name" => env("LUCENT_NAME", "Lucent"),
|
||||||
|
"url" => env("LUCENT_URL", env('APP_URL')),
|
||||||
|
"previewTarget" => env("LUCENT_PREVIEW_TARGET", "previewTarget"),
|
||||||
|
/*
|
||||||
|
* Make available laravel artisan commands for admin users
|
||||||
|
* example:
|
||||||
|
* [
|
||||||
|
* "command1:signature" => "Description 1"
|
||||||
|
* "command2:signature" => "Description 2"
|
||||||
|
* ]
|
||||||
|
*
|
||||||
|
* */
|
||||||
|
"commands" => [],
|
||||||
|
/*
|
||||||
|
* Image filter will be available both for rich editor fields
|
||||||
|
* and throughout your application
|
||||||
|
*
|
||||||
|
* example:
|
||||||
|
* [
|
||||||
|
* "filterName" => Filter::class
|
||||||
|
* ]
|
||||||
|
*
|
||||||
|
* */
|
||||||
|
"imageFilters" => [],
|
||||||
|
"canInvite" => ["admin"],
|
||||||
|
"canBuild" => ["admin"],
|
||||||
|
"systemUserId" => "",
|
||||||
|
"schemaFields" => [
|
||||||
|
\Lucent\Schema\Ui\Checkbox::class,
|
||||||
|
\Lucent\Schema\Ui\Color::class,
|
||||||
|
\Lucent\Schema\Ui\Date::class,
|
||||||
|
\Lucent\Schema\Ui\Datetime::class,
|
||||||
|
\Lucent\Schema\Ui\File::class,
|
||||||
|
\Lucent\Schema\Ui\Json::class,
|
||||||
|
\Lucent\Schema\Ui\Markdown::class,
|
||||||
|
\Lucent\Schema\Ui\Number::class,
|
||||||
|
\Lucent\Schema\Ui\Reference::class,
|
||||||
|
\Lucent\Schema\Ui\Rich::class,
|
||||||
|
\Lucent\Schema\Ui\Slug::class,
|
||||||
|
\Lucent\Schema\Ui\Text::class,
|
||||||
|
\Lucent\Schema\Ui\Textarea::class
|
||||||
|
],
|
||||||
|
"renderers" => [
|
||||||
|
"row" => [
|
||||||
|
"file" => \Lucent\Schema\Renderer\Row\File::class,
|
||||||
|
"slug" => \Lucent\Schema\Renderer\Row\Text::class,
|
||||||
|
"text" => \Lucent\Schema\Renderer\Row\Text::class,
|
||||||
|
"checkbox" => \Lucent\Schema\Renderer\Row\Text::class,
|
||||||
|
"number" => \Lucent\Schema\Renderer\Row\Text::class,
|
||||||
|
"rich" => \Lucent\Schema\Renderer\Row\Text::class,
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
+5
-11
@@ -24,7 +24,8 @@ There are 3 types of schemas
|
|||||||
- **fields**: The list of your fields. Look the field reference for more
|
- **fields**: The list of your fields. Look the field reference for more
|
||||||
- **isEntry**: If this schema is important, it will show be visible on the main the sidebar. Default: false _optional_
|
- **isEntry**: If this schema is important, it will show be visible on the main the sidebar. Default: false _optional_
|
||||||
- **sortBy**: The default sorting in the content browser _optional_
|
- **sortBy**: The default sorting in the content browser _optional_
|
||||||
- **titleTemplate**: Mustache code to customize the preview field _optional_
|
- **cardTitle**: Mustache code to customize the preview field _optional_
|
||||||
|
- **cardImage**: Field name of image you want to use as a preview image _optional_
|
||||||
- **revisions**: How many revisions are going to be kept for each record _optional_
|
- **revisions**: How many revisions are going to be kept for each record _optional_
|
||||||
- **read**: Array of user groups that have read permissions _optional_
|
- **read**: Array of user groups that have read permissions _optional_
|
||||||
- **write**: Array of user groups that have write permissions _optional_
|
- **write**: Array of user groups that have write permissions _optional_
|
||||||
@@ -40,20 +41,12 @@ There are 3 types of schemas
|
|||||||
- **fields**: The list of your fields. Look the field reference for more
|
- **fields**: The list of your fields. Look the field reference for more
|
||||||
- **isEntry**: If this schema is important, it will show be visible on the main the sidebar _optional_
|
- **isEntry**: If this schema is important, it will show be visible on the main the sidebar _optional_
|
||||||
- **sortBy**: The default sorting in the content browser _optional_
|
- **sortBy**: The default sorting in the content browser _optional_
|
||||||
- **titleTemplate**: Mustache code to customize the preview field _optional_
|
- **cardTitle**: Mustache code to customize the preview field _optional_
|
||||||
- **revisions**: How many revisions are going to be kept for each record _optional_
|
- **revisions**: How many revisions are going to be kept for each record _optional_
|
||||||
- **read**: Array of user groups that have read permissions _optional_
|
- **read**: Array of user groups that have read permissions _optional_
|
||||||
- **write**: Array of user groups that have write permissions _optional_
|
- **write**: Array of user groups that have write permissions _optional_
|
||||||
|
|
||||||
|
|
||||||
## Block Reference
|
|
||||||
|
|
||||||
- **name**: The ID of the collection. Camelcase and plural is the recommended format ex. blogPosts
|
|
||||||
- **label**: The friendly name of the schema
|
|
||||||
- **type**: The type of the collection. Should be "block"
|
|
||||||
- **fields**: The list of your fields. Look the field reference for more
|
|
||||||
|
|
||||||
|
|
||||||
A full Collection example without the fields:
|
A full Collection example without the fields:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
@@ -74,7 +67,8 @@ A full Collection example without the fields:
|
|||||||
"SEO"
|
"SEO"
|
||||||
],
|
],
|
||||||
"sortBy": "-_sys.createdAt",
|
"sortBy": "-_sys.createdAt",
|
||||||
"titleTemplate": "{{name}} {{slug}}",
|
"schemaTitle": "{{name}} {{slug}}",
|
||||||
|
"schemaImage": "cover",
|
||||||
"revisions": 15,
|
"revisions": 15,
|
||||||
"read": [
|
"read": [
|
||||||
"admin",
|
"admin",
|
||||||
|
|||||||
Vendored
-198
File diff suppressed because one or more lines are too long
Vendored
+342
File diff suppressed because one or more lines are too long
Vendored
-1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+2
-2
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"main.js": {
|
"main.js": {
|
||||||
"file": "assets/main-0h36XLim.js",
|
"file": "assets/main-BJyanQ7P.js",
|
||||||
"name": "main",
|
"name": "main",
|
||||||
"src": "main.js",
|
"src": "main.js",
|
||||||
"isEntry": true,
|
"isEntry": true,
|
||||||
"css": [
|
"css": [
|
||||||
"assets/main-CaexgiEy.css"
|
"assets/main-Dk7njt4m.css"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import {onClickOutside} from "./../helpers/clickOutside.js";
|
||||||
|
|
||||||
|
export function dropdown() {
|
||||||
|
document.querySelectorAll(".dropdown").forEach(el => {
|
||||||
|
dropdownInit(el);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function dropdownInit(el) {
|
||||||
|
const button = el.querySelector("button");
|
||||||
|
const menu = el.querySelector(".dropdown-menu");
|
||||||
|
button.addEventListener('click', function () {
|
||||||
|
if (menu.hasAttribute('hidden')) {
|
||||||
|
this.setAttribute('aria-expanded', 'true');
|
||||||
|
menu.removeAttribute('hidden');
|
||||||
|
|
||||||
|
// Set focus on first link
|
||||||
|
// will be highlighted for keyboard users
|
||||||
|
menu.querySelector(".dropdown-item:first-child")?.focus();
|
||||||
|
} else {
|
||||||
|
menu.setAttribute('hidden', 'true');
|
||||||
|
this.setAttribute('aria-expanded', 'false');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('keydown', (event) => {
|
||||||
|
// Ignore IME composition
|
||||||
|
if (event.isComposing || event.key === "esc") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close menu with ESC key
|
||||||
|
if (event.keyCode === 27) {
|
||||||
|
if (!menu.hasAttribute('hidden')) {
|
||||||
|
menu.setAttribute('aria-expanded', 'false');
|
||||||
|
menu.setAttribute('hidden', 'true');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onClickOutside(menu, ".dropdown", () => menu.hidden = true);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
export function onClickOutside(ele, closest, cb) {
|
||||||
|
document.addEventListener('click', function (event) {
|
||||||
|
if (!event.target.closest(closest)) {
|
||||||
|
cb(event)
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
|
};
|
||||||
|
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
export function throttle(delay, callback, options) {
|
||||||
|
const {
|
||||||
|
noTrailing = false,
|
||||||
|
noLeading = false,
|
||||||
|
debounceMode = undefined
|
||||||
|
} = options || {};
|
||||||
|
|
||||||
|
let timeoutID;
|
||||||
|
let cancelled = false;
|
||||||
|
let lastExec = 0;
|
||||||
|
|
||||||
|
function clearExistingTimeout() {
|
||||||
|
if (timeoutID) {
|
||||||
|
clearTimeout(timeoutID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancel(options) {
|
||||||
|
const {upcomingOnly = false} = options || {};
|
||||||
|
clearExistingTimeout();
|
||||||
|
cancelled = !upcomingOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
function wrapper(...arguments_) {
|
||||||
|
let self = this;
|
||||||
|
let elapsed = Date.now() - lastExec;
|
||||||
|
|
||||||
|
if (cancelled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
function exec() {
|
||||||
|
lastExec = Date.now();
|
||||||
|
callback.apply(self, arguments_);
|
||||||
|
}
|
||||||
|
|
||||||
|
function clear() {
|
||||||
|
timeoutID = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!noLeading && debounceMode && !timeoutID) {
|
||||||
|
exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
clearExistingTimeout();
|
||||||
|
|
||||||
|
if (debounceMode === undefined && elapsed > delay) {
|
||||||
|
if (noLeading) {
|
||||||
|
lastExec = Date.now();
|
||||||
|
if (!noTrailing) {
|
||||||
|
timeoutID = setTimeout(debounceMode ? clear : exec, delay);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
exec();
|
||||||
|
}
|
||||||
|
} else if (noTrailing !== true) {
|
||||||
|
timeoutID = setTimeout(
|
||||||
|
debounceMode ? clear : exec,
|
||||||
|
debounceMode === undefined ? delay - elapsed : delay
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapper.cancel = cancel;
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function debounce(delay, callback, options) {
|
||||||
|
const {atBegin = false} = options || {};
|
||||||
|
return throttle(delay, callback, {debounceMode: atBegin !== false});
|
||||||
|
}
|
||||||
@@ -4,6 +4,21 @@ import Account from "./svelte/Account.svelte";
|
|||||||
import Channel from "./svelte/Channel.svelte";
|
import Channel from "./svelte/Channel.svelte";
|
||||||
import Mustache from "mustache";
|
import Mustache from "mustache";
|
||||||
import 'htmx.org';
|
import 'htmx.org';
|
||||||
|
import {dropdown} from "./components/dropdown.js";
|
||||||
|
import {colorPicker} from "./recordEditor/colorPicker.js";
|
||||||
|
import {sortReferences} from "./recordEditor/sortReferences.js";
|
||||||
|
import {recordDialog} from "./recordEditor/recordDialog.js";
|
||||||
|
import {createRecordEntry} from "./recordEditor/createRecordEntry.js";
|
||||||
|
import {editRecordEntry} from "./recordEditor/editRecordEntry.js";
|
||||||
|
|
||||||
|
addEventListener("load", (event) => {
|
||||||
|
dropdown()
|
||||||
|
colorPicker()
|
||||||
|
sortReferences()
|
||||||
|
recordDialog()
|
||||||
|
createRecordEntry()
|
||||||
|
editRecordEntry()
|
||||||
|
});
|
||||||
|
|
||||||
Mustache.escape = function (value) {
|
Mustache.escape = function (value) {
|
||||||
return value;
|
return value;
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
export function colorPicker() {
|
||||||
|
document.querySelectorAll(".color-picker").forEach(el => {
|
||||||
|
colorPickerInit(el);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function colorPickerInit(el){
|
||||||
|
const colorInput = el.querySelector("[type=color]");
|
||||||
|
const textInput = el.querySelector("[type=text]");
|
||||||
|
|
||||||
|
colorInput.addEventListener("change",(e) => textInput.value = colorInput.value);
|
||||||
|
textInput.addEventListener("change",(e) => colorInput.value = textInput.value);
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
export function createRecordEntry() {
|
||||||
|
const createButton = document.getElementById("record-create-button");
|
||||||
|
if(!createButton){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
createButton.addEventListener("click", save)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function save(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const recordForm = document.getElementById("record-form");
|
||||||
|
let validationErrors = null;
|
||||||
|
let errorMessage = "";
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const schemaName = urlParams.get("schema")
|
||||||
|
console.log("SAVE: Attempt");
|
||||||
|
|
||||||
|
let formData = new FormData(recordForm)
|
||||||
|
|
||||||
|
axios
|
||||||
|
.post("/lucent/records", {
|
||||||
|
schema: schemaName,
|
||||||
|
data: Object.fromEntries(formData),
|
||||||
|
status: "draft",
|
||||||
|
// edges: graph.edges,
|
||||||
|
isCreateMode: true,
|
||||||
|
})
|
||||||
|
.then(function (response) {
|
||||||
|
console.log("SAVE: SAVED");
|
||||||
|
window.location = "/lucent/recordss/" + record.id;
|
||||||
|
return;
|
||||||
|
})
|
||||||
|
.catch(function (error) {
|
||||||
|
if (!error?.response) {
|
||||||
|
}
|
||||||
|
if (typeof error?.response.data.error === "string") {
|
||||||
|
errorMessage = error.response.data.error;
|
||||||
|
} else {
|
||||||
|
validationErrors = error.response.data.error;
|
||||||
|
console.log(validationErrors)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
export function editRecordEntry() {
|
||||||
|
const saveButton = document.getElementById("record-save-button");
|
||||||
|
if(!saveButton){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
saveButton.addEventListener("click", save)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function save(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const recordForm = document.getElementById("record-form");
|
||||||
|
// let validationErrors = null;
|
||||||
|
// let errorMessage = "";
|
||||||
|
console.log("SAVE: Attempt");
|
||||||
|
//
|
||||||
|
let formData = new FormData(recordForm)
|
||||||
|
//
|
||||||
|
axios
|
||||||
|
.post("/lucent/records", {
|
||||||
|
id: recordForm.dataset.recordId,
|
||||||
|
data: Object.fromEntries(formData),
|
||||||
|
status: "draft",
|
||||||
|
// edges: graph.edges,
|
||||||
|
isCreateMode: false,
|
||||||
|
})
|
||||||
|
.then(function (response) {
|
||||||
|
console.log("SAVE: SAVED");
|
||||||
|
})
|
||||||
|
.catch(function (error) {
|
||||||
|
// if (!error?.response) {
|
||||||
|
// }
|
||||||
|
// if (typeof error?.response.data.error === "string") {
|
||||||
|
// errorMessage = error.response.data.error;
|
||||||
|
// } else {
|
||||||
|
// validationErrors = error.response.data.error;
|
||||||
|
// console.log(validationErrors)
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
export function recordDialog() {
|
||||||
|
document.querySelectorAll("[data-open-modal]").forEach(el => {
|
||||||
|
const schema = el.dataset.openModal
|
||||||
|
el.addEventListener("click", e => {
|
||||||
|
e.preventDefault()
|
||||||
|
load(schema)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function load(schema) {
|
||||||
|
axios
|
||||||
|
.get("/lucent/content/" + schema)
|
||||||
|
.then((response) => {
|
||||||
|
|
||||||
|
const dialogWrapperEl = document.createElement("div");
|
||||||
|
dialogWrapperEl.innerHTML = response.data;
|
||||||
|
document.body.appendChild(dialogWrapperEl);
|
||||||
|
const dialogEl = dialogWrapperEl.querySelector("dialog");
|
||||||
|
dialogEl.showModal();
|
||||||
|
dialogWrapperEl.querySelector(".close").addEventListener("click", e => dialogEl.close());
|
||||||
|
|
||||||
|
dialogEl.addEventListener("close", (event) => {
|
||||||
|
dialogWrapperEl.remove();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((error) => console.log(error));
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import Sortable from "sortablejs";
|
||||||
|
export function sortReferences() {
|
||||||
|
document.querySelectorAll(".color-picker").forEach(el => {
|
||||||
|
|
||||||
|
let options = {
|
||||||
|
animation: 150, // ms, animation speed moving items when sorting, `0` — without animation
|
||||||
|
easing: "cubic-bezier(1, 0, 0, 1)",
|
||||||
|
direction: 'vertical',
|
||||||
|
onUpdate: function (/**Event*/ evt) {
|
||||||
|
// dispatch("update", {
|
||||||
|
// source: evt.oldIndex,
|
||||||
|
// target: evt.newIndex,
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Sortable.create(el, options);
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
import Login from "./account/Login.svelte";
|
import Login from "./account/Login.svelte";
|
||||||
import Verify from "./account/Verify.svelte";
|
import Verify from "./account/Verify.svelte";
|
||||||
import Profile from "./account/Profile.svelte";
|
import Profile from "./account/Profile.svelte";
|
||||||
|
import SetupIndex from "./setup/Index.svelte";
|
||||||
import {setContext} from "svelte";
|
import {setContext} from "svelte";
|
||||||
|
|
||||||
const components = {
|
const components = {
|
||||||
@@ -10,6 +11,7 @@
|
|||||||
login: Login,
|
login: Login,
|
||||||
verify: Verify,
|
verify: Verify,
|
||||||
profile: Profile,
|
profile: Profile,
|
||||||
|
setup: SetupIndex,
|
||||||
};
|
};
|
||||||
|
|
||||||
export let title;
|
export let title;
|
||||||
@@ -22,7 +24,7 @@
|
|||||||
setContext("user", user);
|
setContext("user", user);
|
||||||
</script>
|
</script>
|
||||||
<div style="text-align: center;background: var(--p20);padding: 20px;color: var(--p90)">
|
<div style="text-align: center;background: var(--p20);padding: 20px;color: var(--p90)">
|
||||||
<h1><a class="text-decoration-none" href="{channel.lucentUrl}">{channel.name}</a></h1>
|
<h1><a class="text-decoration-none" href="{channel.lucentUrl}">{channel.name ?? "Lucent Setup"}</a></h1>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<svelte:component this={components[view]} {title} {...data}/>
|
<svelte:component this={components[view]} {title} {...data}/>
|
||||||
|
|||||||
@@ -38,13 +38,13 @@
|
|||||||
|
|
||||||
|
|
||||||
<div class="main-wrapper">
|
<div class="main-wrapper">
|
||||||
<div class="sidebar-content">
|
<div class="sidebar-content">
|
||||||
<Navbar schema={data.schema}/>
|
<Navbar schema={data.schema}/>
|
||||||
</div>
|
</div>
|
||||||
<div class="main-content">
|
<div class="main-content">
|
||||||
<Header />
|
<Header/>
|
||||||
<svelte:component this={components[view]} {title} {...data}/>
|
<svelte:component this={components[view]} {title} {...data}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +1,25 @@
|
|||||||
<script>
|
<script>
|
||||||
import {getContext, onMount} from "svelte";
|
import {getContext, onMount} from "svelte";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
const channel = getContext("channel");
|
const channel = getContext("channel");
|
||||||
export let title;
|
export let title;
|
||||||
|
export let command;
|
||||||
$: date = "";
|
$: date = "";
|
||||||
$: logs = "";
|
$: logs = "";
|
||||||
|
|
||||||
|
let anchorEl;
|
||||||
let inProgress = false;
|
let inProgress = false;
|
||||||
|
|
||||||
function connect() {
|
function connect() {
|
||||||
const eventSource = new EventSource(channel.lucentUrl + "/build-report-source");
|
const eventSource = new EventSource(channel.lucentUrl + "/command-report-source/" + command.signature );
|
||||||
|
|
||||||
eventSource.onmessage = function (event) {
|
eventSource.onmessage = function (event) {
|
||||||
inProgress = true;
|
inProgress = true;
|
||||||
const data = JSON.parse(event.data);
|
const data = JSON.parse(event.data);
|
||||||
date = data.date;
|
date = data.date;
|
||||||
logs = data.logs;
|
logs = data.logs;
|
||||||
|
anchorEl.scrollIntoView()
|
||||||
}
|
}
|
||||||
eventSource.onerror = (e) => {
|
eventSource.onerror = (e) => {
|
||||||
console.log(e)
|
console.log(e)
|
||||||
@@ -28,8 +31,7 @@
|
|||||||
function buildWebsite(e) {
|
function buildWebsite(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
inProgress = true;
|
inProgress = true;
|
||||||
|
axios.post(channel.lucentUrl + "/command/" + command.signature).then(response => {
|
||||||
axios.post(channel.lucentUrl + "/build").then(response => {
|
|
||||||
connect()
|
connect()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -46,26 +48,36 @@
|
|||||||
|
|
||||||
<h3 class="header-small mb-5">{title}</h3>
|
<h3 class="header-small mb-5">{title}</h3>
|
||||||
|
|
||||||
<button on:click={buildWebsite} class="button primary mb-3" disabled={inProgress}>Start Build
|
<button on:click={buildWebsite} class="button primary mb-3" disabled={inProgress}>Start
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
{#if inProgress}
|
{#if inProgress}
|
||||||
<span class="badge text-bg-warning">
|
<span class="badge text-bg-warning">
|
||||||
Build in progress
|
Action in progress
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
{#if !inProgress && logs}
|
{#if !inProgress && logs}
|
||||||
<span class="badge text-bg-info">
|
<span class="badge text-bg-info">
|
||||||
Build completed
|
Action completed
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<pre>{logs}</pre>
|
<pre class="logs">{logs}
|
||||||
|
<div bind:this={anchorEl}> </div>
|
||||||
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<style>
|
||||||
|
.logs{
|
||||||
|
max-height: 70vh;
|
||||||
|
overflow: scroll;
|
||||||
|
background: var(--p90);
|
||||||
|
color: var(--p10);
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -113,8 +113,21 @@
|
|||||||
path: '<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14M5 12l4-4m-4 4 4 4"/>',
|
path: '<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14M5 12l4-4m-4 4 4 4"/>',
|
||||||
viewBox: "0 0 24 24",
|
viewBox: "0 0 24 24",
|
||||||
},
|
},
|
||||||
|
"list": {
|
||||||
|
path: '<path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M9 8h10M9 12h10M9 16h10M4.99 8H5m-.02 4h.01m0 4H5"/>',
|
||||||
|
viewBox: "0 0 24 24",
|
||||||
|
},
|
||||||
|
"ordered-list": {
|
||||||
|
path: '<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6h8m-8 6h8m-8 6h8M4 16a2 2 0 1 1 3.321 1.5L4 20h5M4 5l2-1v6m-2 0h4"/>',
|
||||||
|
viewBox: "0 0 24 24",
|
||||||
|
},
|
||||||
|
"italic": {
|
||||||
|
path: '<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m8.874 19 6.143-14M6 19h6.33m-.66-14H18"/>',
|
||||||
|
viewBox: "0 0 24 24",
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export let width = 16;
|
export let width = 16;
|
||||||
export let height = 16;
|
export let height = 16;
|
||||||
export let icon = "";
|
export let icon = "";
|
||||||
|
|||||||
@@ -83,7 +83,11 @@
|
|||||||
{#if record._file?.path}
|
{#if record._file?.path}
|
||||||
<div class="file-table-row">
|
<div class="file-table-row">
|
||||||
<Preview record={record} size={record._file?.width > 0 ? "medium" : "small"}/>
|
<Preview record={record} size={record._file?.width > 0 ? "medium" : "small"}/>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
{#if record.status === "draft"}
|
||||||
|
<span style="text-transform: uppercase;font-size:10px">{record.status}</span>
|
||||||
|
{/if}
|
||||||
<a
|
<a
|
||||||
href="{channel.lucentUrl}/records/{record.id}"
|
href="{channel.lucentUrl}/records/{record.id}"
|
||||||
target={inModal ? "_blank" : "_self"}
|
target={inModal ? "_blank" : "_self"}
|
||||||
@@ -109,6 +113,9 @@
|
|||||||
href="{channel.lucentUrl}/records/{record.id}"
|
href="{channel.lucentUrl}/records/{record.id}"
|
||||||
target={inModal ? "_blank" : "_self"}
|
target={inModal ? "_blank" : "_self"}
|
||||||
>
|
>
|
||||||
|
{#if record.status === "draft"}
|
||||||
|
<span style="text-transform: uppercase;font-size:10px">{record.status}</span>
|
||||||
|
{/if}
|
||||||
{previewTitle(channel.schemas, record, graph)}
|
{previewTitle(channel.schemas, record, graph)}
|
||||||
</a>
|
</a>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
<div class="references">
|
<div class="references">
|
||||||
{#each recordEdges as recordEdge}
|
{#each recordEdges as recordEdge}
|
||||||
<span class="mr-3">
|
<span class="reference">
|
||||||
<PreviewCardSmall {schemas} {graph} record={recordEdge}/>
|
<PreviewCardSmall {schemas} {graph} record={recordEdge}/>
|
||||||
</span>
|
</span>
|
||||||
{/each}
|
{/each}
|
||||||
|
|||||||
@@ -11,7 +11,8 @@
|
|||||||
/* max-width: 128px; */
|
/* max-width: 128px; */
|
||||||
max-height: 24px;
|
max-height: 24px;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
/* white-space: nowrap; */
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
/* white-space: nowrap; */
|
||||||
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
export let inModal;
|
export let inModal;
|
||||||
export let modalUrl;
|
export let modalUrl;
|
||||||
export let graph;
|
export let graph;
|
||||||
|
|
||||||
let filter = {
|
let filter = {
|
||||||
label: "",
|
label: "",
|
||||||
operator: "",
|
operator: "",
|
||||||
@@ -58,6 +57,7 @@
|
|||||||
const filterRecord = extractFilterRecord(graph, value);
|
const filterRecord = extractFilterRecord(graph, value);
|
||||||
|
|
||||||
function extractFilterRecord(graph, value) {
|
function extractFilterRecord(graph, value) {
|
||||||
|
|
||||||
if (!filter.isReference) {
|
if (!filter.isReference) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -82,7 +82,7 @@
|
|||||||
{#if filter.isReference && filterRecord}
|
{#if filter.isReference && filterRecord}
|
||||||
{filter.label} is {previewTitle(channel.schemas, filterRecord)}
|
{filter.label} is {previewTitle(channel.schemas, filterRecord)}
|
||||||
{:else}
|
{:else}
|
||||||
{filter.label} {operators.find((o) => o.name === filter.operator)?.symbol ?? ""} {value}
|
{filter.label} {operators.find((o) => o.name === filter.operator)?.symbol ?? ""} {operators.find((o) => o.name === filter.operator)?.hasValue ? value : ""}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
export let inModal;
|
export let inModal;
|
||||||
export let modalUrl;
|
export let modalUrl;
|
||||||
|
|
||||||
|
|
||||||
let dropdown;
|
let dropdown;
|
||||||
let search = "";
|
let search = "";
|
||||||
let systemFieldsFiltered = systemFields;
|
let systemFieldsFiltered = systemFields;
|
||||||
@@ -70,6 +69,13 @@
|
|||||||
activeOperator = operators.find(o => o.name === "eq")
|
activeOperator = operators.find(o => o.name === "eq")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function selectOperator(e, operator) {
|
||||||
|
activeOperator = operator;
|
||||||
|
if (!operator.hasValue) {
|
||||||
|
applyFilter(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function applyFilter(e) {
|
function applyFilter(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
let filterPrefix = "";
|
let filterPrefix = "";
|
||||||
@@ -146,7 +152,7 @@
|
|||||||
<div class="selected-filter">field: {activeField.label}</div>
|
<div class="selected-filter">field: {activeField.label}</div>
|
||||||
|
|
||||||
{#each activeOperators as operator}
|
{#each activeOperators as operator}
|
||||||
<button class="dropdown-item button" on:click={e => activeOperator = operator }>
|
<button class="dropdown-item button" on:click={e => selectOperator(e,operator)}>
|
||||||
{operator.label}
|
{operator.label}
|
||||||
</button>
|
</button>
|
||||||
{/each}
|
{/each}
|
||||||
@@ -214,8 +220,8 @@
|
|||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<button class="button applied-filter">
|
<button class="button applied-filter">
|
||||||
Submit
|
Submit
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -1,27 +1,24 @@
|
|||||||
|
export function imgurl(channel, record) {
|
||||||
export function imgurl(channel,record) {
|
|
||||||
|
|
||||||
if (record._file.mime === "image/svg+xml") {
|
if (record._file.mime === "image/svg+xml") {
|
||||||
return fileurl(channel, record);
|
return fileurl(channel, record);
|
||||||
}
|
}
|
||||||
return channel.filesUrl + `/thumbs/${record._file.path}`;
|
return channel.disks[record._file.disk] + `/thumbs/${record._file.path}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fileurl(channel, record) {
|
export function fileurl(channel, record) {
|
||||||
return channel.filesUrl + `/${record._file.path}`;
|
return channel.disks[record._file.disk] + `/${record._file.path}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function htmlurl(channel,record, preset) {
|
export function htmlurl(channel, record, preset) {
|
||||||
|
|
||||||
let html = "";
|
let html = "";
|
||||||
let url = fileurl(channel,record)
|
let url = fileurl(channel, record)
|
||||||
|
|
||||||
if (record._file.width > 0) {
|
if (record._file.width > 0) {
|
||||||
let presetUrl = url;
|
let presetUrl = url;
|
||||||
if (preset) {
|
if (preset) {
|
||||||
presetUrl = channel.filesUrl + `/templates/${preset}/${record._file.path}`;
|
presetUrl = channel.disks[record._file.disk] + `/templates/${preset}/${record._file.path}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
html = `<img src="${presetUrl}" alt="${record._file.path}" />`
|
html = `<img src="${presetUrl}" alt="${record._file.path}" />`
|
||||||
} else if (record._file.mime === "image/svg+xml") {
|
} else if (record._file.mime === "image/svg+xml") {
|
||||||
html = `<img src="${url}" alt="${record._file.path}"/>`
|
html = `<img src="${url}" alt="${record._file.path}"/>`
|
||||||
|
|||||||
@@ -1,17 +1,25 @@
|
|||||||
<script>
|
<script>
|
||||||
import Avatar from "../account/Avatar.svelte";
|
import Avatar from "../account/Avatar.svelte";
|
||||||
import {getContext} from "svelte";
|
import {getContext} from "svelte";
|
||||||
|
import Dropdown from "../common/Dropdown.svelte";
|
||||||
|
|
||||||
const channel = getContext("channel");
|
const channel = getContext("channel");
|
||||||
const user = getContext("user");
|
const user = getContext("user");
|
||||||
|
console.log( channel.commands)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<div class="top-nav ">
|
<div class="top-nav ">
|
||||||
<a class="top-nav-item" href="{channel.lucentUrl}/members">Members</a>
|
<a class="top-nav-item" href="{channel.lucentUrl}/members">Members</a>
|
||||||
|
|
||||||
{#if channel.generateCommand}
|
{#if channel.commands.length > 0}
|
||||||
<a href="{channel.lucentUrl}/build-report" class="top-nav-item">Build website</a>
|
<Dropdown>
|
||||||
|
<div slot="button">Actions</div>
|
||||||
|
{#each channel.commands as command}
|
||||||
|
<a href="{channel.lucentUrl}/command-report/{command.signature}" class="top-nav-item">{command.name}</a>
|
||||||
|
{/each}
|
||||||
|
</Dropdown>
|
||||||
|
|
||||||
{/if}
|
{/if}
|
||||||
<!-- <div>-->
|
<!-- <div>-->
|
||||||
<!-- <form method="GET">-->
|
<!-- <form method="GET">-->
|
||||||
@@ -19,8 +27,8 @@
|
|||||||
<!-- class="form-control" required/>-->
|
<!-- class="form-control" required/>-->
|
||||||
<!-- </form>-->
|
<!-- </form>-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
<a href="{channel.lucentUrl}/profile">
|
<a href="{channel.lucentUrl}/profile">
|
||||||
<Avatar side="28" name={user.name}/>
|
<Avatar side="28" name={user.name}/>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<script>
|
<script>
|
||||||
|
|
||||||
// https://codesandbox.io/s/codemirror-remark-editor-4m4z9?file=/src/CodeEditor.js:374-387
|
// https://codesandbox.io/s/codemirror-remark-editor-4m4z9?file=/src/CodeEditor.js:374-387
|
||||||
import {onMount, onDestroy} from "svelte";
|
import {onDestroy, onMount} from "svelte";
|
||||||
import {basicSetup, EditorView} from "codemirror";
|
import {basicSetup, EditorView} from "codemirror";
|
||||||
import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
|
import {autocompletion, completionKeymap} from "@codemirror/autocomplete";
|
||||||
import {EditorState, Compartment} from "@codemirror/state";
|
import {Compartment, EditorState} from "@codemirror/state";
|
||||||
import {keymap} from "@codemirror/view";
|
import {keymap} from "@codemirror/view";
|
||||||
import {indentWithTab} from "@codemirror/commands";
|
import {indentWithTab} from "@codemirror/commands";
|
||||||
import {markdown} from "@codemirror/lang-markdown";
|
import {markdown} from "@codemirror/lang-markdown";
|
||||||
@@ -15,6 +15,29 @@
|
|||||||
export let value;
|
export let value;
|
||||||
export let editable = true;
|
export let editable = true;
|
||||||
|
|
||||||
|
export function insertMedia(info) {
|
||||||
|
let insertText = "";
|
||||||
|
if (info.record._file.width > 0) {
|
||||||
|
insertText = ``;
|
||||||
|
} else {
|
||||||
|
insertText = `[${info.record._file.originalName}](${info.originalUrl})`;
|
||||||
|
}
|
||||||
|
const cursor = codeMirrorView.state.selection.main.head;
|
||||||
|
const transaction = codeMirrorView.state.update({
|
||||||
|
changes: {
|
||||||
|
from: cursor,
|
||||||
|
insert: insertText,
|
||||||
|
},
|
||||||
|
// the next 2 lines will set the appropriate cursor position after inserting the new text.
|
||||||
|
selection: {anchor: cursor + 1},
|
||||||
|
scrollIntoView: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (transaction) {
|
||||||
|
codeMirrorView.dispatch(transaction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
let language = new Compartment();
|
let language = new Compartment();
|
||||||
let tabSize = new Compartment();
|
let tabSize = new Compartment();
|
||||||
@@ -51,7 +74,6 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
|
|||||||
@@ -100,8 +100,8 @@
|
|||||||
tinymce.init({...config, ...additionalConfig});
|
tinymce.init({...config, ...additionalConfig});
|
||||||
});
|
});
|
||||||
|
|
||||||
export function insertMedia(html){
|
export function insertMedia(info){
|
||||||
activeEditor.execCommand('InsertHTML', false, html);
|
activeEditor.execCommand('InsertHTML', false, info.html);
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -3,9 +3,12 @@
|
|||||||
import {Editor} from '@tiptap/core'
|
import {Editor} from '@tiptap/core'
|
||||||
import Document from '@tiptap/extension-document'
|
import Document from '@tiptap/extension-document'
|
||||||
import Paragraph from '@tiptap/extension-paragraph'
|
import Paragraph from '@tiptap/extension-paragraph'
|
||||||
|
import Dropcursor from '@tiptap/extension-dropcursor'
|
||||||
import Text from '@tiptap/extension-text'
|
import Text from '@tiptap/extension-text'
|
||||||
import Heading from '@tiptap/extension-heading'
|
import Heading from '@tiptap/extension-heading'
|
||||||
|
import HardBreak from '@tiptap/extension-hard-break'
|
||||||
import Blockquote from '@tiptap/extension-blockquote';
|
import Blockquote from '@tiptap/extension-blockquote';
|
||||||
|
import CodeBlock from '@tiptap/extension-code-block';
|
||||||
import Bold from '@tiptap/extension-bold';
|
import Bold from '@tiptap/extension-bold';
|
||||||
import BulletList from '@tiptap/extension-bullet-list';
|
import BulletList from '@tiptap/extension-bullet-list';
|
||||||
import Code from '@tiptap/extension-code';
|
import Code from '@tiptap/extension-code';
|
||||||
@@ -19,6 +22,8 @@
|
|||||||
import TableCell from '@tiptap/extension-table-cell';
|
import TableCell from '@tiptap/extension-table-cell';
|
||||||
import TableHeader from '@tiptap/extension-table-header';
|
import TableHeader from '@tiptap/extension-table-header';
|
||||||
import Underline from '@tiptap/extension-underline';
|
import Underline from '@tiptap/extension-underline';
|
||||||
|
import Image from '@tiptap/extension-image';
|
||||||
|
import Icon from "../common/Icon.svelte";
|
||||||
|
|
||||||
let element;
|
let element;
|
||||||
let editor;
|
let editor;
|
||||||
@@ -32,19 +37,22 @@
|
|||||||
Paragraph,
|
Paragraph,
|
||||||
Text,
|
Text,
|
||||||
Bold,
|
Bold,
|
||||||
|
ListItem,
|
||||||
BulletList,
|
BulletList,
|
||||||
Code,
|
Code,
|
||||||
|
CodeBlock,
|
||||||
History,
|
History,
|
||||||
Italic,
|
Italic,
|
||||||
ListItem,
|
HardBreak,
|
||||||
OrderedList,
|
OrderedList,
|
||||||
ListItem,
|
|
||||||
Strike,
|
Strike,
|
||||||
Table,
|
Table,
|
||||||
TableRow,
|
TableRow,
|
||||||
TableCell,
|
TableCell,
|
||||||
TableHeader,
|
TableHeader,
|
||||||
Underline,
|
Underline,
|
||||||
|
Dropcursor,
|
||||||
|
Image,
|
||||||
Heading.configure({
|
Heading.configure({
|
||||||
levels: [1, 2, 3],
|
levels: [1, 2, 3],
|
||||||
}),
|
}),
|
||||||
@@ -56,7 +64,11 @@
|
|||||||
// force re-render so `editor.isActive` works as expected
|
// force re-render so `editor.isActive` works as expected
|
||||||
editor = editor;
|
editor = editor;
|
||||||
},
|
},
|
||||||
|
onUpdate: ({editor}) => {
|
||||||
|
value = editor.getHTML()
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
@@ -64,33 +76,99 @@
|
|||||||
editor.destroy();
|
editor.destroy();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export function insertMedia(info){
|
||||||
|
editor.chain().focus().setImage({ src: info.url }).run()
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if editor}
|
{#if editor}
|
||||||
<button
|
<div class="editor-toolbar">
|
||||||
on:click={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}
|
<button
|
||||||
class:active={editor.isActive('heading', { level: 1 })}
|
class="button"
|
||||||
>
|
on:click={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}
|
||||||
H1
|
class:active={editor.isActive('heading', { level: 1 })}
|
||||||
</button>
|
>
|
||||||
<button
|
H1
|
||||||
on:click={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}
|
</button>
|
||||||
class:active={editor.isActive('heading', { level: 2 })}
|
<button
|
||||||
>
|
class="button"
|
||||||
H2
|
on:click={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}
|
||||||
</button>
|
class:active={editor.isActive('heading', { level: 2 })}
|
||||||
<button
|
>
|
||||||
on:click={() => editor.chain().focus().setParagraph().run()}
|
H2
|
||||||
class:active={editor.isActive('paragraph')}
|
</button>
|
||||||
>
|
|
||||||
P
|
<button
|
||||||
</button>
|
class="button"
|
||||||
<button
|
on:click={() => editor.chain().focus().toggleBold().run()}
|
||||||
on:click={() => editor.chain().focus().toggleBold().run()}
|
class:active={editor.isActive('bold')}
|
||||||
class:active={editor.isActive('bold')}
|
>
|
||||||
>
|
B
|
||||||
Bold
|
</button>
|
||||||
</button>
|
<button
|
||||||
|
class="button"
|
||||||
|
on:click={() => editor.chain().focus().toggleItalic().run()}
|
||||||
|
class:active={editor.isActive('italic')}
|
||||||
|
>
|
||||||
|
<em>IT</em>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button"
|
||||||
|
on:click={() => editor.chain().focus().toggleUnderline().run()}
|
||||||
|
class:active={editor.isActive('underline')}
|
||||||
|
>
|
||||||
|
<u>U</u>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button"
|
||||||
|
on:click={() => editor.chain().focus().toggleStrike().run()}
|
||||||
|
class:active={editor.isActive('strike')}
|
||||||
|
>
|
||||||
|
<s>S</s>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button"
|
||||||
|
on:click={() => editor.commands.unsetAllMarks()}
|
||||||
|
>
|
||||||
|
Clear
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button"
|
||||||
|
on:click={() => editor.chain().focus().toggleCode().run()}
|
||||||
|
class:active={editor.isActive('code')}
|
||||||
|
>
|
||||||
|
Code
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button"
|
||||||
|
on:click={() => editor.chain().focus().toggleBulletList().run()}
|
||||||
|
class:active={editor.isActive('bulletList')}
|
||||||
|
>
|
||||||
|
<Icon icon="list"></Icon>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button"
|
||||||
|
on:click={() => editor.chain().focus().toggleOrderedList().run()}
|
||||||
|
class:active={editor.isActive('orderedList')}
|
||||||
|
>
|
||||||
|
<Icon icon="ordered-list"></Icon>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button"
|
||||||
|
on:click={() => editor.chain().focus().toggleBlockquote().run()}
|
||||||
|
class:active={editor.isActive('blockquote')}
|
||||||
|
>
|
||||||
|
""
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button"
|
||||||
|
on:click={() => editor.chain().focus().toggleCodeBlock().run()}
|
||||||
|
class:active={editor.isActive('codeBlock')}
|
||||||
|
>
|
||||||
|
cb
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div bind:this={element} class="content"/>
|
<div bind:this={element} class="content"/>
|
||||||
@@ -1,21 +1,68 @@
|
|||||||
<script>
|
<script>
|
||||||
import {onMount} from "svelte";
|
import {onDestroy, onMount} from "svelte";
|
||||||
import Trix from "trix"
|
import Trix from "trix"
|
||||||
import customcss from "./tinymce.css?inline";
|
|
||||||
import "trix/dist/trix.css"
|
import "trix/dist/trix.css"
|
||||||
|
|
||||||
export let value = "";
|
export let value = "";
|
||||||
let textareaEl;
|
export let field;
|
||||||
let lastVal;
|
let editor;
|
||||||
let editorWrapper;
|
|
||||||
let activeEditor;
|
|
||||||
|
function updateValue(e) {
|
||||||
|
value = e.target.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function insertMedia(info){
|
||||||
|
if(info.record._file.width > 0){
|
||||||
|
var attachment = new Trix.Attachment({ content: info.html })
|
||||||
|
editor.editor.insertAttachment(attachment)
|
||||||
|
}else{
|
||||||
|
editor.editor.insertHTML(`<a href="${info.originalUrl}">${info.record._file.originalName}</a>`)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
editor.addEventListener("trix-file-accept", (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
})
|
||||||
|
|
||||||
|
editor.addEventListener("trix-before-initialize", (e) => {
|
||||||
|
Trix.config.blockAttributes.heading1.tagName = 'h2';
|
||||||
|
const { toolbarElement } = e.target
|
||||||
|
const h1Button = toolbarElement.querySelector("[data-trix-attribute=heading1]")
|
||||||
|
h1Button.insertAdjacentHTML("afterend", `<button style="text-indent: initial;padding: 14px 10px !important;" type="button" class="trix-button trix-button--icon" data-trix-attribute="heading3" title="Heading 3" tabindex="-1" data-trix-active="">H3</button>`)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
// onDestroy(() => {
|
||||||
|
// editor.removeEventListener("trix-before-initialize")
|
||||||
|
// })
|
||||||
|
|
||||||
|
|
||||||
Trix.config.blockAttributes.default.breakOnReturn = false
|
Trix.config.blockAttributes.default.breakOnReturn = false
|
||||||
console.log(Trix.config)
|
Trix.config.blockAttributes.heading3 = {
|
||||||
|
tagName: 'h3',
|
||||||
|
terminal: true,
|
||||||
|
breakOnReturn: true,
|
||||||
|
group: false
|
||||||
|
}
|
||||||
|
// console.log(Trix.config)
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div bind:this={editorWrapper} class="tox-wrapper">
|
<div class="tox-wrapper">
|
||||||
<input bind:this={textareaEl} id="x" bind:value type="hidden">
|
<input id="x-{field.name}" {value} type="hidden">
|
||||||
<trix-editor class="trix-content content" input="x"></trix-editor>
|
<trix-editor
|
||||||
|
bind:this={editor}
|
||||||
|
class=" content"
|
||||||
|
input="x-{field.name}"
|
||||||
|
role="textbox"
|
||||||
|
tabindex="0"
|
||||||
|
on:trix-change={updateValue}
|
||||||
|
|
||||||
|
></trix-editor>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -98,6 +98,16 @@
|
|||||||
bind:graph
|
bind:graph
|
||||||
{record}
|
{record}
|
||||||
/>
|
/>
|
||||||
|
{:else if field.info.name === "markdown"}
|
||||||
|
<Markdown
|
||||||
|
bind:value={data[field.name]}
|
||||||
|
{schema}
|
||||||
|
{field}
|
||||||
|
{validationErrors}
|
||||||
|
{isCreateMode}
|
||||||
|
bind:graph
|
||||||
|
{record}
|
||||||
|
/>
|
||||||
{:else}
|
{:else}
|
||||||
<svelte:component
|
<svelte:component
|
||||||
this={formElement}
|
this={formElement}
|
||||||
|
|||||||
@@ -15,11 +15,14 @@
|
|||||||
let backlinks = graph.parentEdges.map(edge => {
|
let backlinks = graph.parentEdges.map(edge => {
|
||||||
let schema = channel.schemas.find((s) => s.name === edge.sourceSchema);
|
let schema = channel.schemas.find((s) => s.name === edge.sourceSchema);
|
||||||
let edgeField = findEdgeField(schema,edge.field);
|
let edgeField = findEdgeField(schema,edge.field);
|
||||||
|
if(!edgeField){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
field: edgeField.label,
|
field: edgeField.label,
|
||||||
record: graph.records.find( record => record.id === edge.source)
|
record: graph.records.find( record => record.id === edge.source)
|
||||||
}
|
}
|
||||||
})
|
}).filter( edgeOrNull => !!edgeOrNull)
|
||||||
</script>
|
</script>
|
||||||
<div class="editor-field">
|
<div class="editor-field">
|
||||||
{#each backlinks as backlink}
|
{#each backlinks as backlink}
|
||||||
|
|||||||
@@ -13,11 +13,8 @@
|
|||||||
{#if record?.data}
|
{#if record?.data}
|
||||||
<a
|
<a
|
||||||
href="{channel.lucentUrl}/records/{record.id}"
|
href="{channel.lucentUrl}/records/{record.id}"
|
||||||
class="text-decoration-none rounded py-1 px-2 d-inline-block"
|
|
||||||
{title}
|
{title}
|
||||||
style="border:2px solid {!schema.color
|
class="reference"
|
||||||
? '#999'
|
|
||||||
: schema.color}!important;white-space: nowrap;"
|
|
||||||
>
|
>
|
||||||
{title}
|
{title}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -9,13 +9,17 @@
|
|||||||
export let value;
|
export let value;
|
||||||
export let isCreateMode;
|
export let isCreateMode;
|
||||||
export let validationErrors;
|
export let validationErrors;
|
||||||
|
|
||||||
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||||
$: errorMessage = getErrorMessage(validationErrors, field.name);
|
$: errorMessage = getErrorMessage(validationErrors, field.name);
|
||||||
|
|
||||||
export let id;
|
export let id;
|
||||||
|
let wrapperDiv;
|
||||||
let pickerInput;
|
let pickerInput;
|
||||||
let pickerInstance;
|
let pickerInstance;
|
||||||
let flatpickrOptions = {
|
let flatpickrOptions = {
|
||||||
|
appendTo: wrapperDiv,
|
||||||
|
static: true,
|
||||||
allowInput: true,
|
allowInput: true,
|
||||||
altInput: true,
|
altInput: true,
|
||||||
altFormat: "Y-m-d H:i:S",
|
altFormat: "Y-m-d H:i:S",
|
||||||
@@ -40,7 +44,7 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="mb-0">
|
<div class="mb-0" bind:this={wrapperDiv}>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
|||||||
@@ -1,21 +1,37 @@
|
|||||||
<script>
|
<script>
|
||||||
import Codemirror from "../../libs/CodemirrorMarkdown.svelte";
|
import Codemirror from "../../libs/CodemirrorMarkdown.svelte";
|
||||||
import { getErrorMessage } from "./errorMessage";
|
import { getErrorMessage } from "./errorMessage";
|
||||||
|
import RichEditorFiles from "./RichEditorFiles.svelte";
|
||||||
|
|
||||||
|
|
||||||
export let value;
|
export let value;
|
||||||
export let field;
|
export let field;
|
||||||
|
export let graph;
|
||||||
|
export let record;
|
||||||
export let isCreateMode;
|
export let isCreateMode;
|
||||||
// export let id;
|
// export let id;
|
||||||
export let validationErrors;
|
export let validationErrors;
|
||||||
$: errorMessage = getErrorMessage(validationErrors, field.name);
|
$: errorMessage = getErrorMessage(validationErrors, field.name);
|
||||||
|
let editor;
|
||||||
|
|
||||||
|
function insertMedia(e){
|
||||||
|
editor.insertMedia(e.detail)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
|
|
||||||
<Codemirror bind:value editable={!field.readonly || isCreateMode} />
|
<Codemirror bind:this={editor} bind:value editable={!field.readonly || isCreateMode} />
|
||||||
|
{#if field.collections.length > 0}
|
||||||
|
<RichEditorFiles
|
||||||
|
bind:graph
|
||||||
|
{record}
|
||||||
|
{field}
|
||||||
|
{validationErrors}
|
||||||
|
on:editor-insert={insertMedia}
|
||||||
|
>
|
||||||
|
</RichEditorFiles>
|
||||||
|
{/if}
|
||||||
{#if errorMessage}
|
{#if errorMessage}
|
||||||
<div class="invalid-feedback d-block">
|
<div class="invalid-feedback d-block">
|
||||||
{errorMessage}
|
{errorMessage}
|
||||||
|
|||||||
@@ -43,6 +43,7 @@
|
|||||||
|
|
||||||
function createInlineReference(e, schemaUId) {
|
function createInlineReference(e, schemaUId) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
inLineCreateRecord = null;
|
||||||
axios
|
axios
|
||||||
.get(channel.lucentUrl + "/records/newInline?schema=" + schemaUId)
|
.get(channel.lucentUrl + "/records/newInline?schema=" + schemaUId)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
@@ -59,27 +60,29 @@
|
|||||||
<div
|
<div
|
||||||
style="display: flex;align-items: center;gap:4px"
|
style="display: flex;align-items: center;gap:4px"
|
||||||
>
|
>
|
||||||
{#each schemas as schema}
|
<Dropdown>
|
||||||
<Dropdown>
|
<div slot="button">New</div>
|
||||||
<div slot="button" class:is-first={!recordId}>
|
{#each schemas as schema}
|
||||||
{schema.label}
|
|
||||||
</div>
|
|
||||||
<button
|
<button
|
||||||
class=" button"
|
class=" button"
|
||||||
on:click={(e) =>
|
on:click={(e) =>
|
||||||
createInlineReference(e, schema.name)}
|
createInlineReference(e, schema.name)}
|
||||||
>Create New Record
|
>{schema.label}
|
||||||
</button>
|
</button>
|
||||||
|
{/each}
|
||||||
|
|
||||||
|
</Dropdown>
|
||||||
|
<Dropdown>
|
||||||
|
<div slot="button"> <Icon icon="magnifying-glass"/></div>
|
||||||
|
{#each schemas as schema}
|
||||||
<button
|
<button
|
||||||
class="button"
|
class="button"
|
||||||
on:click={(e) => openBrowseModal(e, schema.name)}
|
on:click={(e) => openBrowseModal(e, schema.name)}
|
||||||
>
|
>{schema.label}
|
||||||
<Icon icon="magnifying-glass"/>
|
</button>
|
||||||
Search
|
{/each}
|
||||||
</button
|
|
||||||
>
|
</Dropdown>
|
||||||
</Dropdown>
|
|
||||||
{/each}
|
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div style="display:flex;align-items: center;gap: 4px">
|
<div style="display:flex;align-items: center;gap: 4px">
|
||||||
@@ -100,9 +103,9 @@
|
|||||||
|
|
||||||
<DialogRecord bind:this={dialogRecord}>
|
<DialogRecord bind:this={dialogRecord}>
|
||||||
{#if inLineCreateRecord}
|
{#if inLineCreateRecord}
|
||||||
|
|
||||||
<InlineEdit
|
<InlineEdit
|
||||||
{...inLineCreateRecord}
|
{...inLineCreateRecord}
|
||||||
|
isCreateMode={true}
|
||||||
on:cancel={(e) => (inLineCreateRecord = null)}
|
on:cancel={(e) => (inLineCreateRecord = null)}
|
||||||
on:inlinesaved={save}
|
on:inlinesaved={save}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import Tinymce from "../../libs/Tinymce.svelte";
|
import Tinymce from "../../libs/Tinymce.svelte";
|
||||||
import RichEditorFiles from "./RichEditorFiles.svelte";
|
import RichEditorFiles from "./RichEditorFiles.svelte";
|
||||||
import {getErrorMessage} from "./errorMessage";
|
import {getErrorMessage} from "./errorMessage";
|
||||||
|
import Trix from "../../libs/Trix.svelte";
|
||||||
|
|
||||||
export let value;
|
export let value;
|
||||||
export let field;
|
export let field;
|
||||||
@@ -24,8 +25,8 @@
|
|||||||
|
|
||||||
<div class="mb-0">
|
<div class="mb-0">
|
||||||
|
|
||||||
|
<Trix {field} bind:this={editor} bind:value></Trix>
|
||||||
<Tinymce bind:this={editor} bind:value {additionalConfig}/>
|
<!-- <Tinymce bind:this={editor} bind:value {additionalConfig}/>-->
|
||||||
{#if field.collections.length > 0}
|
{#if field.collections.length > 0}
|
||||||
<RichEditorFiles
|
<RichEditorFiles
|
||||||
bind:graph
|
bind:graph
|
||||||
@@ -38,7 +39,7 @@
|
|||||||
|
|
||||||
</RichEditorFiles>
|
</RichEditorFiles>
|
||||||
{/if}
|
{/if}
|
||||||
<!-- <TipTap bind:value />-->
|
|
||||||
|
|
||||||
{#if errorMessage}
|
{#if errorMessage}
|
||||||
<div class="invalid-feedback d-block">
|
<div class="invalid-feedback d-block">
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
<script>
|
<script>
|
||||||
import {sortByField} from "../../edges/sortEdges";
|
|
||||||
import Sortable from "../../libs/Sortable.svelte";
|
|
||||||
import PreviewFile from "../previews/PreviewFile.svelte";
|
import PreviewFile from "../previews/PreviewFile.svelte";
|
||||||
import Dropdown from "../../common/Dropdown.svelte";
|
import Dropdown from "../../common/Dropdown.svelte";
|
||||||
import Dialog from "../../dialog/Dialog.svelte";
|
import Dialog from "../../dialog/Dialog.svelte";
|
||||||
@@ -70,7 +68,8 @@
|
|||||||
{#each references as reference (reference.id)}
|
{#each references as reference (reference.id)}
|
||||||
<!--This div helps the sorting thing-->
|
<!--This div helps the sorting thing-->
|
||||||
<div>
|
<div>
|
||||||
<PreviewFile record={reference} hasDelete={true} hasInsert={true} on:remove={removeReference} on:editor-insert></PreviewFile>
|
<PreviewFile record={reference} hasDelete={true} hasInsert={true} on:remove={removeReference}
|
||||||
|
on:editor-insert></PreviewFile>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
<div style="display: flex;align-items: center; gap:10px;">
|
<div style="display: flex;align-items: center; gap:10px;">
|
||||||
{#if !isCreateMode}
|
{#if !isCreateMode}
|
||||||
<Dropdown >
|
<Dropdown>
|
||||||
<div slot="button">
|
<div slot="button">
|
||||||
<Icon icon="ellipsis"/>
|
<Icon icon="ellipsis"/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
import {createEventDispatcher, getContext} from "svelte";
|
import {createEventDispatcher, getContext} from "svelte";
|
||||||
import Preview from "../../files/Preview.svelte";
|
import Preview from "../../files/Preview.svelte";
|
||||||
import {previewTitle} from "./../Preview";
|
import {previewTitle} from "./../Preview";
|
||||||
import {htmlurl} from "../../files/imageserver.js"
|
import {fileurl, htmlurl} from "../../files/imageserver.js"
|
||||||
import Status from "./../Status.svelte";
|
import Status from "./../Status.svelte";
|
||||||
import Dropdown from "../../common/Dropdown.svelte";
|
import Dropdown from "../../common/Dropdown.svelte";
|
||||||
|
|
||||||
@@ -25,8 +25,14 @@
|
|||||||
|
|
||||||
function insert(e, preset) {
|
function insert(e, preset) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
let html = htmlurl(channel,record, preset)
|
let html = htmlurl(channel, record, preset)
|
||||||
dispatch("editor-insert", html);
|
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>
|
</script>
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<script>
|
||||||
|
import Step from "./Step.svelte"
|
||||||
|
|
||||||
|
export let steps;
|
||||||
|
export let allSuccess = false;
|
||||||
|
|
||||||
|
console.log(steps);
|
||||||
|
</script>
|
||||||
|
<div class="wrapper-tiny">
|
||||||
|
|
||||||
|
{#each steps as step}
|
||||||
|
<Step {step}></Step>
|
||||||
|
{/each}
|
||||||
|
|
||||||
|
<div style="text-align: center;margin-top: 30px;">
|
||||||
|
{#if allSuccess}
|
||||||
|
<a href="/lucent/register" class="bt">Create the first user</a>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
<script>
|
||||||
|
import Icon from "../common/Icon.svelte"
|
||||||
|
|
||||||
|
export let step;
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="step step-{step.status}">
|
||||||
|
<div class="step-icon">
|
||||||
|
{#if step.status === "success"}
|
||||||
|
<Icon icon="check"></Icon>
|
||||||
|
{:else}
|
||||||
|
<Icon icon="close"></Icon>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<div style="width:100%">
|
||||||
|
<h4>{step.name}</h4>
|
||||||
|
<details>
|
||||||
|
<summary>Instuctions</summary>
|
||||||
|
<code class="instructions">{step.instructions}</code>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.step-success .step-icon{
|
||||||
|
background: var(--suc10);
|
||||||
|
color: var(--suc100);
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-fail .step-icon{
|
||||||
|
background: var(--err10);
|
||||||
|
color: var(--err100);
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-icon{
|
||||||
|
padding: 12px;
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: start;
|
||||||
|
gap: 10px;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
padding: 12px;
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
details {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.instructions {
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 12px;
|
||||||
|
border-radius: 12px;
|
||||||
|
background: var(--p10);
|
||||||
|
white-space: break-spaces;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Generated
+2562
-587
File diff suppressed because it is too large
Load Diff
+3
-20
@@ -11,40 +11,23 @@
|
|||||||
"@codemirror/lang-markdown": "^6.2.5",
|
"@codemirror/lang-markdown": "^6.2.5",
|
||||||
"@codemirror/state": "^6.4.1",
|
"@codemirror/state": "^6.4.1",
|
||||||
"@sveltejs/vite-plugin-svelte": "^3.1.1",
|
"@sveltejs/vite-plugin-svelte": "^3.1.1",
|
||||||
"@tiptap/core": "^2.6.4",
|
|
||||||
"@tiptap/extension-blockquote": "^2.6.4",
|
|
||||||
"@tiptap/extension-bold": "^2.6.4",
|
|
||||||
"@tiptap/extension-bullet-list": "^2.6.4",
|
|
||||||
"@tiptap/extension-code": "^2.6.4",
|
|
||||||
"@tiptap/extension-document": "^2.6.4",
|
|
||||||
"@tiptap/extension-heading": "^2.6.4",
|
|
||||||
"@tiptap/extension-history": "^2.6.4",
|
|
||||||
"@tiptap/extension-italic": "^2.6.4",
|
|
||||||
"@tiptap/extension-list-item": "^2.6.4",
|
|
||||||
"@tiptap/extension-ordered-list": "^2.6.4",
|
|
||||||
"@tiptap/extension-paragraph": "^2.6.4",
|
|
||||||
"@tiptap/extension-strike": "^2.6.4",
|
|
||||||
"@tiptap/extension-table": "^2.6.4",
|
|
||||||
"@tiptap/extension-table-cell": "^2.6.4",
|
|
||||||
"@tiptap/extension-table-header": "^2.6.4",
|
|
||||||
"@tiptap/extension-table-row": "^2.6.4",
|
|
||||||
"@tiptap/extension-text": "^2.6.4",
|
|
||||||
"@tiptap/extension-underline": "^2.6.4",
|
|
||||||
"@tiptap/pm": "^2.6.4",
|
|
||||||
"axios": "^1.7.4",
|
"axios": "^1.7.4",
|
||||||
"codemirror": "^6.0.1",
|
"codemirror": "^6.0.1",
|
||||||
"date-fns": "^3.6.0",
|
"date-fns": "^3.6.0",
|
||||||
"flatpickr": "^4.6.13",
|
"flatpickr": "^4.6.13",
|
||||||
"fuse.js": "^7.0.0",
|
"fuse.js": "^7.0.0",
|
||||||
"htmx.org": "^2.0.1",
|
"htmx.org": "^2.0.1",
|
||||||
|
"install": "^0.13.0",
|
||||||
"laravel-vite-plugin": "^1.0.5",
|
"laravel-vite-plugin": "^1.0.5",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mustache": "^4.2.0",
|
"mustache": "^4.2.0",
|
||||||
|
"npm": "^10.8.2",
|
||||||
"postcss": "8.4.31",
|
"postcss": "8.4.31",
|
||||||
"sass": "^1.77.8",
|
"sass": "^1.77.8",
|
||||||
"sortablejs": "^1.15.2",
|
"sortablejs": "^1.15.2",
|
||||||
"svelte": "^4.2.18",
|
"svelte": "^4.2.18",
|
||||||
"tinymce": "^6.8.4",
|
"tinymce": "^6.8.4",
|
||||||
|
"trix": "^2.1.5",
|
||||||
"uuid": "^10.0.0",
|
"uuid": "^10.0.0",
|
||||||
"vite": "5.2.6"
|
"vite": "5.2.6"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,11 +7,16 @@
|
|||||||
|
|
||||||
.cm-content{
|
.cm-content{
|
||||||
background-color: var(--p10);
|
background-color: var(--p10);
|
||||||
|
color: var(--p100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-content{
|
.cm-content{
|
||||||
background-color: var(--p20);
|
background-color: var(--p20);
|
||||||
|
|
||||||
|
}
|
||||||
|
.ͼ4 .cm-line ::selection, .ͼ4 .cm-line::selection{
|
||||||
|
background: var(--p40) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-activeLine{
|
.cm-activeLine{
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
|
||||||
|
.flatpickr-wrapper {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-field {
|
||||||
|
.flatpickr-calendar {
|
||||||
|
border-radius: 12px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flatpickr-months .flatpickr-month {
|
||||||
|
background: var(--p30);
|
||||||
|
color: var(--text);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flatpickr-current-month .flatpickr-monthDropdown-months {
|
||||||
|
background: var(--p30);
|
||||||
|
}
|
||||||
|
|
||||||
|
.flatpickr-weekdays{
|
||||||
|
background: var(--p30);
|
||||||
|
color: var(--text);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.flatpickr-weekdaycontainer .flatpickr-weekday{
|
||||||
|
background: var(--p30);
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.flatpickr-days{
|
||||||
|
background: var(--p10);
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.flatpickr-time{
|
||||||
|
background: var(--p10);
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ body:has(dialog[open]) {
|
|||||||
|
|
||||||
dialog {
|
dialog {
|
||||||
margin: 2vh auto;
|
margin: 2vh auto;
|
||||||
background-color: #fff;
|
background-color: var(--p10);
|
||||||
padding: 34px;
|
padding: 34px;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
@@ -49,6 +49,6 @@ dialog::backdrop {
|
|||||||
position: sticky;
|
position: sticky;
|
||||||
top: -34px;
|
top: -34px;
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
background: #fff;
|
background-color: var(--p10);
|
||||||
padding: 10px 0;
|
padding: 10px 0;
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
overflow: visible;
|
overflow: visible;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
z-index: 20;
|
z-index: 22;
|
||||||
background: var(--p20);
|
background: var(--p20);
|
||||||
transition: 600ms;
|
transition: 600ms;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
.record-edit {
|
.record-edit {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
max-width: 900px;
|
||||||
.invalid-feedback {
|
.invalid-feedback {
|
||||||
color: var(--text-error);
|
color: var(--text-error);
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
margin: 6px 0;
|
margin: 6px 0;
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
|
|
||||||
.button {
|
.button:not(.primary) {
|
||||||
background: var(--p30);
|
background: var(--p30);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
@@ -53,9 +53,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
dialog {
|
dialog {
|
||||||
.button {
|
.button:not(.primary) {
|
||||||
background: var(--p20);
|
background: var(--p20);
|
||||||
|
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: var(--p30);
|
background: var(--p30);
|
||||||
}
|
}
|
||||||
@@ -143,3 +144,4 @@
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,150 @@
|
|||||||
|
.tiptap {
|
||||||
|
width: 100%;
|
||||||
|
background: var(--p20);
|
||||||
|
border: 1px solid var(--p50);
|
||||||
|
border-radius: 0 0 5px 5px;
|
||||||
|
padding: 15px 15px;
|
||||||
|
font-size: 16px;
|
||||||
|
|
||||||
|
:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
background: var(--p10);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
&.ProseMirror-selectednode {
|
||||||
|
box-shadow: 0 0 1px 2px var(--p70);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-field {
|
||||||
|
.editor-toolbar {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
background: var(--p30);
|
||||||
|
border-radius: 5px 5px 0 0;
|
||||||
|
padding: 5px 7px;
|
||||||
|
|
||||||
|
.button:not(.primary) {
|
||||||
|
font-weight: 700;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background: var(--p40);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
.tiptap {
|
||||||
|
li > p {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
trix-editor {
|
||||||
|
background: var(--p20)!important;
|
||||||
|
border: 1px solid var(--p50)!important;
|
||||||
|
border-radius: 0 0 5px 5px!important;
|
||||||
|
padding: 15px 15px!important;
|
||||||
|
& > div {
|
||||||
|
margin-bottom: 14px;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 23px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
background: var(--p10)!important;
|
||||||
|
|
||||||
|
}
|
||||||
|
figure.attachment{
|
||||||
|
display: flex!important;
|
||||||
|
flex-direction: column!important;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
.attachment {
|
||||||
|
background: var(--p20);
|
||||||
|
padding: 12px 0;
|
||||||
|
text-align: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
img {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-trix-mutable].attachment img {
|
||||||
|
box-shadow: 0 0 1px 2px var(--p70) !important;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.trix-button--remove {
|
||||||
|
box-shadow: none !important;
|
||||||
|
border: 2px solid var(--p40) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trix-button--remove:hover {
|
||||||
|
border: 2px solid var(--p40);
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--p80);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trix-toolbar {
|
||||||
|
.trix-button-row {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trix-button-group {
|
||||||
|
background: transparent !important;
|
||||||
|
border: none !important;
|
||||||
|
display: flex !important;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
.trix-button-group--history-tools,.trix-button-group--file-tools
|
||||||
|
{
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trix-button {
|
||||||
|
border-radius: 6px !important;
|
||||||
|
background: var(--p30) !important;
|
||||||
|
padding: 14px 22px !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 0px solid var(--p30) !important;
|
||||||
|
font-size: 14px !important;
|
||||||
|
min-height: 27px !important;
|
||||||
|
display: flex !important;
|
||||||
|
align-items: center !important;
|
||||||
|
gap: 4px;
|
||||||
|
color: var(--text) !important;
|
||||||
|
|
||||||
|
&:before{
|
||||||
|
background-size: 22px!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover{
|
||||||
|
background: var(--p40) !important;
|
||||||
|
}
|
||||||
|
&.trix-active{
|
||||||
|
background: var(--p50) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,10 +29,14 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
background: var(--p30);
|
background: var(--p30);
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
padding: 3px 12px 6px;
|
padding: 3px 12px 3px;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
|
&:focus{
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: var(--p40);
|
background: var(--p40);
|
||||||
|
|||||||
+17
-1
@@ -55,7 +55,11 @@
|
|||||||
border: none;
|
border: none;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
|
&.field-ui-number,&.field-ui-slug,&.field-ui-text,&.field-ui-rich,&.field-ui-url{
|
||||||
|
max-height: 24px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
//img{
|
//img{
|
||||||
// width: 48px;
|
// width: 48px;
|
||||||
//}
|
//}
|
||||||
@@ -113,6 +117,18 @@
|
|||||||
.field-ui-number {
|
.field-ui-number {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.references{
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
.reference{
|
||||||
|
font-size: 13px;
|
||||||
|
border-radius: 12px;
|
||||||
|
background: var(--p30);
|
||||||
|
padding: 1px 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-table-row {
|
.file-table-row {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
margin: 20px 0 20px;
|
margin: 20px 0 20px;
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
|
flex-wrap: wrap;
|
||||||
.tab{
|
.tab{
|
||||||
list-style: none;
|
list-style: none;
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,20 @@
|
|||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
h1{
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 34px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2{
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3{
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
padding: 0 0 0 16px;
|
padding: 0 0 0 16px;
|
||||||
@@ -31,6 +45,54 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
code{
|
||||||
|
background: var(--p30);
|
||||||
|
padding: 0 6px;
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
img{
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote{
|
||||||
|
border:1px solid var(--p30);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 12px 40px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::before{
|
||||||
|
content: "\201C";
|
||||||
|
color: var(--p60);
|
||||||
|
font-size:4em;
|
||||||
|
position: absolute;
|
||||||
|
left: 10px;
|
||||||
|
top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after{
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
background: var(--grey-light);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
color: var(--white);
|
||||||
|
font-family: 'JetBrainsMono', monospace;
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
|
||||||
|
code {
|
||||||
|
background: none;
|
||||||
|
color: inherit;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.lx-small-text {
|
.lx-small-text {
|
||||||
|
|||||||
@@ -53,6 +53,7 @@
|
|||||||
@import "./table";
|
@import "./table";
|
||||||
@import "./avatar";
|
@import "./avatar";
|
||||||
@import "./codemirror";
|
@import "./codemirror";
|
||||||
|
@import "./rich";
|
||||||
@import "./layout";
|
@import "./layout";
|
||||||
@import "./wrappers";
|
@import "./wrappers";
|
||||||
@import "./toolbar";
|
@import "./toolbar";
|
||||||
@@ -69,6 +70,7 @@
|
|||||||
@import "./reference-tags";
|
@import "./reference-tags";
|
||||||
@import "./members";
|
@import "./members";
|
||||||
@import "./revisions";
|
@import "./revisions";
|
||||||
|
@import "./datepicker";
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background-color: var(--p10);
|
background-color: var(--p10);
|
||||||
@@ -104,3 +106,6 @@ a {
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
<h2 class="mb-5">Enter Lucent</h2>
|
<h2 class="mb-5">Enter Lucent</h2>
|
||||||
|
|
||||||
<form hx-post="/lucent/login" >
|
<form hx-post="/lucent/login" >
|
||||||
|
@csrf
|
||||||
<p>Submit your email address and you will receive a <b>login link</b> to your email</p>
|
<p>Submit your email address and you will receive a <b>login link</b> to your email</p>
|
||||||
<p>Don't forget to check your spam folder</p>
|
<p>Don't forget to check your spam folder</p>
|
||||||
<div class="mt-5 mb-3">
|
<div class="mt-5 mb-3">
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
return strtoupper($segs[0][0]).strtoupper($segs[0][1]);
|
return strtoupper($segs[0][0]).strtoupper($segs[0][1]);
|
||||||
};
|
};
|
||||||
|
|
||||||
$name = $user["name"];
|
$name = (string)data_get($user,"name");
|
||||||
$charIndex = ord($name[1]) + strlen($name);
|
$charIndex = ord($name[1]) + strlen($name);
|
||||||
$colorIndex = $charIndex % 19;
|
$colorIndex = $charIndex % 19;
|
||||||
$bgColor = $colors[$colorIndex];
|
$bgColor = $colors[$colorIndex];
|
||||||
@@ -39,5 +39,5 @@
|
|||||||
title="{{$name}}"
|
title="{{$name}}"
|
||||||
style="background-color:{{$bgColor}};height: {{$side}}px;width: {{$side}}px; font-size:{{$side / 2}}px"
|
style="background-color:{{$bgColor}};height: {{$side}}px;width: {{$side}}px; font-size:{{$side / 2}}px"
|
||||||
>
|
>
|
||||||
<div class="avatar__letters">{{$initials($user["name"])}}</div>
|
<div class="avatar__letters">{{$initials($name)}}</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<div class="checkbox-wrapper">
|
||||||
|
<input id="c1-13" type="checkbox" value="{{$value}}" {{$indeterminate ?? false ? "indeterminate" : ""}} {{$checked ?? false ? "checked" : ""}} />
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<div class="dropdown">
|
||||||
|
<button
|
||||||
|
class="button dropdown-button"
|
||||||
|
type="button"
|
||||||
|
aria-expanded="false"
|
||||||
|
>
|
||||||
|
{{$slot}}
|
||||||
|
</button>
|
||||||
|
<div class="dropdown-menu orientation-{orientation}" hidden>
|
||||||
|
{{$items}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
@php
|
||||||
|
$icons = [
|
||||||
|
"trash-can"=> [
|
||||||
|
"path"=> '<path d="M135.2 17.69C140.6 6.848 151.7 0 163.8 0H284.2C296.3 0 307.4 6.848 312.8 17.69L320 32H416C433.7 32 448 46.33 448 64C448 81.67 433.7 96 416 96H32C14.33 96 0 81.67 0 64C0 46.33 14.33 32 32 32H128L135.2 17.69zM31.1 128H416V448C416 483.3 387.3 512 352 512H95.1C60.65 512 31.1 483.3 31.1 448V128zM111.1 208V432C111.1 440.8 119.2 448 127.1 448C136.8 448 143.1 440.8 143.1 432V208C143.1 199.2 136.8 192 127.1 192C119.2 192 111.1 199.2 111.1 208zM207.1 208V432C207.1 440.8 215.2 448 223.1 448C232.8 448 240 440.8 240 432V208C240 199.2 232.8 192 223.1 192C215.2 192 207.1 199.2 207.1 208zM304 208V432C304 440.8 311.2 448 320 448C328.8 448 336 440.8 336 432V208C336 199.2 328.8 192 320 192C311.2 192 304 199.2 304 208z"/>',
|
||||||
|
"viewBox"=> "0 0 448 512",
|
||||||
|
],
|
||||||
|
"circle-chevron-down"=> [
|
||||||
|
"path"=> '<path d="M256 0C114.6 0 0 114.6 0 256c0 141.4 114.6 256 256 256s256-114.6 256-256C512 114.6 397.4 0 256 0zM390.6 246.6l-112 112C272.4 364.9 264.2 368 256 368s-16.38-3.125-22.62-9.375l-112-112c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L256 290.8l89.38-89.38c12.5-12.5 32.75-12.5 45.25 0S403.1 234.1 390.6 246.6z"/>',
|
||||||
|
"viewBox"=> "0 0 512 512",
|
||||||
|
],
|
||||||
|
"circle-chevron-up"=> [
|
||||||
|
"path"=> '<path d="M256 0C114.6 0 0 114.6 0 256c0 141.4 114.6 256 256 256s256-114.6 256-256C512 114.6 397.4 0 256 0zM390.6 310.6c-12.5 12.5-32.75 12.5-45.25 0L256 221.3L166.6 310.6c-12.5 12.5-32.75 12.5-45.25 0s-12.5-32.75 0-45.25l112-112C239.6 147.1 247.8 144 256 144s16.38 3.125 22.62 9.375l112 112C403.1 277.9 403.1 298.1 390.6 310.6z"/>',
|
||||||
|
"viewBox"=> "0 0 512 512",
|
||||||
|
],
|
||||||
|
"ellipsis"=> [
|
||||||
|
"path"=> '<path d="M120 256C120 286.9 94.93 312 64 312C33.07 312 8 286.9 8 256C8 225.1 33.07 200 64 200C94.93 200 120 225.1 120 256zM280 256C280 286.9 254.9 312 224 312C193.1 312 168 286.9 168 256C168 225.1 193.1 200 224 200C254.9 200 280 225.1 280 256zM328 256C328 225.1 353.1 200 384 200C414.9 200 440 225.1 440 256C440 286.9 414.9 312 384 312C353.1 312 328 286.9 328 256z"/>',
|
||||||
|
"viewBox"=> "0 0 448 512",
|
||||||
|
],
|
||||||
|
"ellipsis-vertical"=> [
|
||||||
|
"path"=> '<path d="M64 360C94.93 360 120 385.1 120 416C120 446.9 94.93 472 64 472C33.07 472 8 446.9 8 416C8 385.1 33.07 360 64 360zM64 200C94.93 200 120 225.1 120 256C120 286.9 94.93 312 64 312C33.07 312 8 286.9 8 256C8 225.1 33.07 200 64 200zM64 152C33.07 152 8 126.9 8 96C8 65.07 33.07 40 64 40C94.93 40 120 65.07 120 96C120 126.9 94.93 152 64 152z"/>',
|
||||||
|
"viewBox"=> "0 0 128 512",
|
||||||
|
],
|
||||||
|
"angles-down"=> [
|
||||||
|
"path"=> '<path d="M169.4 278.6C175.6 284.9 183.8 288 192 288s16.38-3.125 22.62-9.375l160-160c12.5-12.5 12.5-32.75 0-45.25s-32.75-12.5-45.25 0L192 210.8L54.63 73.38c-12.5-12.5-32.75-12.5-45.25 0s-12.5 32.75 0 45.25L169.4 278.6zM329.4 265.4L192 402.8L54.63 265.4c-12.5-12.5-32.75-12.5-45.25 0s-12.5 32.75 0 45.25l160 160C175.6 476.9 183.8 480 192 480s16.38-3.125 22.62-9.375l160-160c12.5-12.5 12.5-32.75 0-45.25S341.9 252.9 329.4 265.4z"/>',
|
||||||
|
"viewBox"=> "0 0 384 512",
|
||||||
|
],
|
||||||
|
"angle-right"=> [
|
||||||
|
"path"=> '<path d="M64 448c-8.188 0-16.38-3.125-22.62-9.375c-12.5-12.5-12.5-32.75 0-45.25L178.8 256L41.38 118.6c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0l160 160c12.5 12.5 12.5 32.75 0 45.25l-160 160C80.38 444.9 72.19 448 64 448z"/>',
|
||||||
|
"viewBox"=> "0 0 256 512",
|
||||||
|
],
|
||||||
|
"photo-film"=> [
|
||||||
|
"path"=> '<path d="M352 432c0 8.836-7.164 16-16 16H176c-8.838 0-16-7.164-16-16L160 128H48C21.49 128 .0003 149.5 .0003 176v288c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48L512 384h-160L352 432zM104 439c0 4.969-4.031 9-9 9h-30c-4.969 0-9-4.031-9-9v-30c0-4.969 4.031-9 9-9h30c4.969 0 9 4.031 9 9V439zM104 335c0 4.969-4.031 9-9 9h-30c-4.969 0-9-4.031-9-9v-30c0-4.969 4.031-9 9-9h30c4.969 0 9 4.031 9 9V335zM104 231c0 4.969-4.031 9-9 9h-30c-4.969 0-9-4.031-9-9v-30C56 196 60.03 192 65 192h30c4.969 0 9 4.031 9 9V231zM408 409c0-4.969 4.031-9 9-9h30c4.969 0 9 4.031 9 9v30c0 4.969-4.031 9-9 9h-30c-4.969 0-9-4.031-9-9V409zM591.1 0H239.1C213.5 0 191.1 21.49 191.1 48v256c0 26.51 21.49 48 48 48h352c26.51 0 48-21.49 48-48v-256C640 21.49 618.5 0 591.1 0zM303.1 64c17.68 0 32 14.33 32 32s-14.32 32-32 32C286.3 128 271.1 113.7 271.1 96S286.3 64 303.1 64zM574.1 279.6C571.3 284.8 565.9 288 560 288H271.1C265.1 288 260.5 284.6 257.7 279.3C255 273.9 255.5 267.4 259.1 262.6l70-96C332.1 162.4 336.9 160 341.1 160c5.11 0 9.914 2.441 12.93 6.574l22.35 30.66l62.74-94.11C442.1 98.67 447.1 96 453.3 96c5.348 0 10.34 2.672 13.31 7.125l106.7 160C576.6 268 576.9 274.3 574.1 279.6z"/>',
|
||||||
|
"viewBox"=> "0 0 640 512",
|
||||||
|
],
|
||||||
|
"file"=> [
|
||||||
|
"path"=> '<path d="M0 64C0 28.65 28.65 0 64 0H224V128C224 145.7 238.3 160 256 160H384V448C384 483.3 355.3 512 320 512H64C28.65 512 0 483.3 0 448V64zM256 128V0L384 128H256z"/>',
|
||||||
|
"viewBox"=> "0 0 384 512",
|
||||||
|
],
|
||||||
|
"circle-info"=> [
|
||||||
|
"path"=> '<path d="M256 0C114.6 0 0 114.6 0 256s114.6 256 256 256s256-114.6 256-256S397.4 0 256 0zM256 128c17.67 0 32 14.33 32 32c0 17.67-14.33 32-32 32S224 177.7 224 160C224 142.3 238.3 128 256 128zM296 384h-80C202.8 384 192 373.3 192 360s10.75-24 24-24h16v-64H224c-13.25 0-24-10.75-24-24S210.8 224 224 224h32c13.25 0 24 10.75 24 24v88h16c13.25 0 24 10.75 24 24S309.3 384 296 384z"/>',
|
||||||
|
"viewBox"=> "0 0 512 512",
|
||||||
|
],
|
||||||
|
"table-columns"=> [
|
||||||
|
"path"=> '<path d="M0 96C0 60.65 28.65 32 64 32H448C483.3 32 512 60.65 512 96V416C512 451.3 483.3 480 448 480H64C28.65 480 0 451.3 0 416V96zM64 416H224V160H64V416zM448 160H288V416H448V160z"/>',
|
||||||
|
"viewBox"=> "0 0 512 512",
|
||||||
|
],
|
||||||
|
"arrow-down-a-z"=> [
|
||||||
|
"path"=> '<path d="M239.6 373.1c11.94-13.05 11.06-33.31-1.969-45.27c-13.55-12.42-33.76-10.52-45.22 1.973L160 366.1V64.03c0-17.7-14.33-32.03-32-32.03S96 46.33 96 64.03v302l-32.4-35.39C51.64 317.7 31.39 316.7 18.38 328.7c-13.03 11.95-13.9 32.22-1.969 45.27l87.1 96.09c12.12 13.26 35.06 13.26 47.19 0L239.6 373.1zM448 416h-50.75l73.38-73.38c9.156-9.156 11.89-22.91 6.938-34.88S460.9 288 447.1 288H319.1C302.3 288 288 302.3 288 320s14.33 32 32 32h50.75l-73.38 73.38c-9.156 9.156-11.89 22.91-6.938 34.88S307.1 480 319.1 480h127.1C465.7 480 480 465.7 480 448S465.7 416 448 416zM492.6 209.3l-79.99-160.1c-10.84-21.81-46.4-21.81-57.24 0L275.4 209.3c-7.906 15.91-1.5 35.24 14.31 43.19c15.87 7.922 35.04 1.477 42.93-14.4l7.154-14.39h88.43l7.154 14.39c6.174 12.43 23.97 23.87 42.93 14.4C494.1 244.6 500.5 225.2 492.6 209.3zM367.8 167.4L384 134.7l16.22 32.63H367.8z"/>',
|
||||||
|
"viewBox"=> "0 0 512 512",
|
||||||
|
],
|
||||||
|
"arrow-up-short-wide"=> [
|
||||||
|
"path"=> '<path d="M544 416h-223.1c-17.67 0-32 14.33-32 32s14.33 32 32 32H544c17.67 0 32-14.33 32-32S561.7 416 544 416zM320 96h32c17.67 0 31.1-14.33 31.1-32s-14.33-32-31.1-32h-32c-17.67 0-32 14.33-32 32S302.3 96 320 96zM320 224H416c17.67 0 32-14.33 32-32s-14.33-32-32-32h-95.1c-17.67 0-32 14.33-32 32S302.3 224 320 224zM320 352H480c17.67 0 32-14.33 32-32s-14.33-32-32-32h-159.1c-17.67 0-32 14.33-32 32S302.3 352 320 352zM151.6 41.95c-12.12-13.26-35.06-13.26-47.19 0l-87.1 96.09C4.475 151.1 5.35 171.4 18.38 183.3c6.141 5.629 13.89 8.414 21.61 8.414c8.672 0 17.3-3.504 23.61-10.39L96 145.9v302C96 465.7 110.3 480 128 480s32-14.33 32-32.03V145.9L192.4 181.3C204.4 194.3 224.6 195.3 237.6 183.3c13.03-11.95 13.9-32.22 1.969-45.27L151.6 41.95z"/>',
|
||||||
|
"viewBox"=> "0 0 576 512",
|
||||||
|
],
|
||||||
|
"arrow-down-wide-short"=> [
|
||||||
|
"path"=> '<path d="M416 288h-95.1c-17.67 0-32 14.33-32 32s14.33 32 32 32H416c17.67 0 32-14.33 32-32S433.7 288 416 288zM544 32h-223.1c-17.67 0-32 14.33-32 32s14.33 32 32 32H544c17.67 0 32-14.33 32-32S561.7 32 544 32zM352 416h-32c-17.67 0-32 14.33-32 32s14.33 32 32 32h32c17.67 0 31.1-14.33 31.1-32S369.7 416 352 416zM480 160h-159.1c-17.67 0-32 14.33-32 32s14.33 32 32 32H480c17.67 0 32-14.33 32-32S497.7 160 480 160zM192.4 330.7L160 366.1V64.03C160 46.33 145.7 32 128 32S96 46.33 96 64.03v302L63.6 330.7c-6.312-6.883-14.94-10.38-23.61-10.38c-7.719 0-15.47 2.781-21.61 8.414c-13.03 11.95-13.9 32.22-1.969 45.27l87.1 96.09c12.12 13.26 35.06 13.26 47.19 0l87.1-96.09c11.94-13.05 11.06-33.31-1.969-45.27C224.6 316.8 204.4 317.7 192.4 330.7z"/>',
|
||||||
|
"viewBox"=> "0 0 576 512",
|
||||||
|
],
|
||||||
|
"filter"=> [
|
||||||
|
"path"=> '<path d="M3.853 54.87C10.47 40.9 24.54 32 40 32H472C487.5 32 501.5 40.9 508.1 54.87C514.8 68.84 512.7 85.37 502.1 97.33L320 320.9V448C320 460.1 313.2 471.2 302.3 476.6C291.5 482 278.5 480.9 268.8 473.6L204.8 425.6C196.7 419.6 192 410.1 192 400V320.9L9.042 97.33C-.745 85.37-2.765 68.84 3.854 54.87L3.853 54.87z"/>',
|
||||||
|
"viewBox"=> "0 0 512 512",
|
||||||
|
],
|
||||||
|
"calendar"=> [
|
||||||
|
"path"=> '<path d="M96 32C96 14.33 110.3 0 128 0C145.7 0 160 14.33 160 32V64H288V32C288 14.33 302.3 0 320 0C337.7 0 352 14.33 352 32V64H400C426.5 64 448 85.49 448 112V160H0V112C0 85.49 21.49 64 48 64H96V32zM448 464C448 490.5 426.5 512 400 512H48C21.49 512 0 490.5 0 464V192H448V464z"/>',
|
||||||
|
"viewBox"=> "0 0 448 512",
|
||||||
|
],
|
||||||
|
"pencil"=> [
|
||||||
|
"path"=> '<path d="M421.7 220.3L188.5 453.4L154.6 419.5L158.1 416H112C103.2 416 96 408.8 96 400V353.9L92.51 357.4C87.78 362.2 84.31 368 82.42 374.4L59.44 452.6L137.6 429.6C143.1 427.7 149.8 424.2 154.6 419.5L188.5 453.4C178.1 463.8 165.2 471.5 151.1 475.6L30.77 511C22.35 513.5 13.24 511.2 7.03 504.1C.8198 498.8-1.502 489.7 .976 481.2L36.37 360.9C40.53 346.8 48.16 333.9 58.57 323.5L291.7 90.34L421.7 220.3zM492.7 58.75C517.7 83.74 517.7 124.3 492.7 149.3L444.3 197.7L314.3 67.72L362.7 19.32C387.7-5.678 428.3-5.678 453.3 19.32L492.7 58.75z"/>',
|
||||||
|
"viewBox"=> "0 0 512 512",
|
||||||
|
],
|
||||||
|
"database"=> [
|
||||||
|
"path"=> '<path d="M448 80V128C448 172.2 347.7 208 224 208C100.3 208 0 172.2 0 128V80C0 35.82 100.3 0 224 0C347.7 0 448 35.82 448 80zM393.2 214.7C413.1 207.3 433.1 197.8 448 186.1V288C448 332.2 347.7 368 224 368C100.3 368 0 332.2 0 288V186.1C14.93 197.8 34.02 207.3 54.85 214.7C99.66 230.7 159.5 240 224 240C288.5 240 348.3 230.7 393.2 214.7V214.7zM54.85 374.7C99.66 390.7 159.5 400 224 400C288.5 400 348.3 390.7 393.2 374.7C413.1 367.3 433.1 357.8 448 346.1V432C448 476.2 347.7 512 224 512C100.3 512 0 476.2 0 432V346.1C14.93 357.8 34.02 367.3 54.85 374.7z"/>',
|
||||||
|
"viewBox"=> "0 0 448 512",
|
||||||
|
],
|
||||||
|
"dice"=> [
|
||||||
|
"path"=> '<path d="M447.1 224c0-12.56-4.781-25.13-14.35-34.76l-174.9-174.9C249.1 4.786 236.5 0 223.1 0C211.4 0 198.9 4.786 189.2 14.35L14.35 189.2C4.783 198.9-.0011 211.4-.0011 223.1c0 12.56 4.785 25.17 14.35 34.8l174.9 174.9c9.625 9.562 22.19 14.35 34.75 14.35s25.13-4.783 34.75-14.35l174.9-174.9C443.2 249.1 447.1 236.6 447.1 224zM96 248c-13.25 0-23.1-10.75-23.1-23.1s10.75-23.1 23.1-23.1S120 210.8 120 224S109.3 248 96 248zM224 376c-13.25 0-23.1-10.75-23.1-23.1s10.75-23.1 23.1-23.1s23.1 10.75 23.1 23.1S237.3 376 224 376zM224 248c-13.25 0-23.1-10.75-23.1-23.1s10.75-23.1 23.1-23.1S248 210.8 248 224S237.3 248 224 248zM224 120c-13.25 0-23.1-10.75-23.1-23.1s10.75-23.1 23.1-23.1s23.1 10.75 23.1 23.1S237.3 120 224 120zM352 248c-13.25 0-23.1-10.75-23.1-23.1s10.75-23.1 23.1-23.1s23.1 10.75 23.1 23.1S365.3 248 352 248zM591.1 192l-118.7 0c4.418 10.27 6.604 21.25 6.604 32.23c0 20.7-7.865 41.38-23.63 57.14l-136.2 136.2v46.37C320 490.5 341.5 512 368 512h223.1c26.5 0 47.1-21.5 47.1-47.1V240C639.1 213.5 618.5 192 591.1 192zM479.1 376c-13.25 0-23.1-10.75-23.1-23.1s10.75-23.1 23.1-23.1s23.1 10.75 23.1 23.1S493.2 376 479.1 376z"/>',
|
||||||
|
"viewBox"=> "0 0 640 512",
|
||||||
|
],
|
||||||
|
|
||||||
|
"triangle-exclamation"=> [
|
||||||
|
"path"=> '<path d="M506.3 417l-213.3-364c-16.33-28-57.54-28-73.98 0l-213.2 364C-10.59 444.9 9.849 480 42.74 480h426.6C502.1 480 522.6 445 506.3 417zM232 168c0-13.25 10.75-24 24-24S280 154.8 280 168v128c0 13.25-10.75 24-23.1 24S232 309.3 232 296V168zM256 416c-17.36 0-31.44-14.08-31.44-31.44c0-17.36 14.07-31.44 31.44-31.44s31.44 14.08 31.44 31.44C287.4 401.9 273.4 416 256 416z"/>',
|
||||||
|
"viewBox"=> "0 0 512 512",
|
||||||
|
],
|
||||||
|
"eye"=> [
|
||||||
|
"path"=> '<path d="M279.6 160.4C282.4 160.1 285.2 160 288 160C341 160 384 202.1 384 256C384 309 341 352 288 352C234.1 352 192 309 192 256C192 253.2 192.1 250.4 192.4 247.6C201.7 252.1 212.5 256 224 256C259.3 256 288 227.3 288 192C288 180.5 284.1 169.7 279.6 160.4zM480.6 112.6C527.4 156 558.7 207.1 573.5 243.7C576.8 251.6 576.8 260.4 573.5 268.3C558.7 304 527.4 355.1 480.6 399.4C433.5 443.2 368.8 480 288 480C207.2 480 142.5 443.2 95.42 399.4C48.62 355.1 17.34 304 2.461 268.3C-.8205 260.4-.8205 251.6 2.461 243.7C17.34 207.1 48.62 156 95.42 112.6C142.5 68.84 207.2 32 288 32C368.8 32 433.5 68.84 480.6 112.6V112.6zM288 112C208.5 112 144 176.5 144 256C144 335.5 208.5 400 288 400C367.5 400 432 335.5 432 256C432 176.5 367.5 112 288 112z"/>',
|
||||||
|
"viewBox"=> "0 0 576 512",
|
||||||
|
],
|
||||||
|
"circle-plus"=> [
|
||||||
|
"path"=> '<path d="M0 256C0 114.6 114.6 0 256 0C397.4 0 512 114.6 512 256C512 397.4 397.4 512 256 512C114.6 512 0 397.4 0 256zM256 368C269.3 368 280 357.3 280 344V280H344C357.3 280 368 269.3 368 256C368 242.7 357.3 232 344 232H280V168C280 154.7 269.3 144 256 144C242.7 144 232 154.7 232 168V232H168C154.7 232 144 242.7 144 256C144 269.3 154.7 280 168 280H232V344C232 357.3 242.7 368 256 368z"/>',
|
||||||
|
"viewBox"=> "0 0 512 512",
|
||||||
|
],
|
||||||
|
"magnifying-glass"=> [
|
||||||
|
"path"=> '<path d="M500.3 443.7l-119.7-119.7c27.22-40.41 40.65-90.9 33.46-144.7C401.8 87.79 326.8 13.32 235.2 1.723C99.01-15.51-15.51 99.01 1.724 235.2c11.6 91.64 86.08 166.7 177.6 178.9c53.8 7.189 104.3-6.236 144.7-33.46l119.7 119.7c15.62 15.62 40.95 15.62 56.57 0C515.9 484.7 515.9 459.3 500.3 443.7zM79.1 208c0-70.58 57.42-128 128-128s128 57.42 128 128c0 70.58-57.42 128-128 128S79.1 278.6 79.1 208z"/>',
|
||||||
|
"viewBox"=> "0 0 512 512",
|
||||||
|
],
|
||||||
|
"expand"=> [
|
||||||
|
"path"=> '<path d="M128 32H32C14.31 32 0 46.31 0 64v96c0 17.69 14.31 32 32 32s32-14.31 32-32V96h64c17.69 0 32-14.31 32-32S145.7 32 128 32zM416 32h-96c-17.69 0-32 14.31-32 32s14.31 32 32 32h64v64c0 17.69 14.31 32 32 32s32-14.31 32-32V64C448 46.31 433.7 32 416 32zM128 416H64v-64c0-17.69-14.31-32-32-32s-32 14.31-32 32v96c0 17.69 14.31 32 32 32h96c17.69 0 32-14.31 32-32S145.7 416 128 416zM416 320c-17.69 0-32 14.31-32 32v64h-64c-17.69 0-32 14.31-32 32s14.31 32 32 32h96c17.69 0 32-14.31 32-32v-96C448 334.3 433.7 320 416 320z"/>',
|
||||||
|
"viewBox"=> "0 0 448 512",
|
||||||
|
],
|
||||||
|
"compress"=> [
|
||||||
|
"path"=> '<path d="M128 320H32c-17.69 0-32 14.31-32 32s14.31 32 32 32h64v64c0 17.69 14.31 32 32 32s32-14.31 32-32v-96C160 334.3 145.7 320 128 320zM416 320h-96c-17.69 0-32 14.31-32 32v96c0 17.69 14.31 32 32 32s32-14.31 32-32v-64h64c17.69 0 32-14.31 32-32S433.7 320 416 320zM320 192h96c17.69 0 32-14.31 32-32s-14.31-32-32-32h-64V64c0-17.69-14.31-32-32-32s-32 14.31-32 32v96C288 177.7 302.3 192 320 192zM128 32C110.3 32 96 46.31 96 64v64H32C14.31 128 0 142.3 0 160s14.31 32 32 32h96c17.69 0 32-14.31 32-32V64C160 46.31 145.7 32 128 32z"/>',
|
||||||
|
"viewBox"=> "0 0 448 512",
|
||||||
|
],
|
||||||
|
"check"=> [
|
||||||
|
"path"=> '<path d="M438.6 105.4C451.1 117.9 451.1 138.1 438.6 150.6L182.6 406.6C170.1 419.1 149.9 419.1 137.4 406.6L9.372 278.6C-3.124 266.1-3.124 245.9 9.372 233.4C21.87 220.9 42.13 220.9 54.63 233.4L159.1 338.7L393.4 105.4C405.9 92.88 426.1 92.88 438.6 105.4H438.6z"/>',
|
||||||
|
"viewBox"=> "0 0 448 512",
|
||||||
|
],
|
||||||
|
"close"=> [
|
||||||
|
"path"=> '<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18 17.94 6M18 18 6.06 6"/>',
|
||||||
|
"viewBox"=> "0 0 24 24",
|
||||||
|
],
|
||||||
|
"arrow-left"=> [
|
||||||
|
"path"=> '<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14M5 12l4-4m-4 4 4 4"/>',
|
||||||
|
"viewBox"=> "0 0 24 24",
|
||||||
|
],
|
||||||
|
"list"=> [
|
||||||
|
"path"=> '<path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M9 8h10M9 12h10M9 16h10M4.99 8H5m-.02 4h.01m0 4H5"/>',
|
||||||
|
"viewBox"=> "0 0 24 24",
|
||||||
|
],
|
||||||
|
"ordered-list"=> [
|
||||||
|
"path"=> '<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6h8m-8 6h8m-8 6h8M4 16a2 2 0 1 1 3.321 1.5L4 20h5M4 5l2-1v6m-2 0h4"/>',
|
||||||
|
"viewBox"=> "0 0 24 24",
|
||||||
|
],
|
||||||
|
"italic"=> [
|
||||||
|
"path"=> '<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m8.874 19 6.143-14M6 19h6.33m-.66-14H18"/>',
|
||||||
|
"viewBox"=> "0 0 24 24",
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<svg
|
||||||
|
class="bi"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="{{$width ?? 16}}"
|
||||||
|
height="{{$height ?? 16}}"
|
||||||
|
viewBox="{{$icons[$icon]["viewBox"]}}"
|
||||||
|
aria-labelledby={icon}
|
||||||
|
role="presentation"
|
||||||
|
stroke="{{$stroke ?? "currentColor"}}"
|
||||||
|
fill="{{$fill ?? "currentColor"}}"
|
||||||
|
|
||||||
|
>
|
||||||
|
{!! $icons[$icon]["path"] !!}
|
||||||
|
</svg>
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<div class="notice {{$type ?? "info"}}">
|
<div class="notice {{$type ?? "info"}}" role="alert">
|
||||||
<div class="title">{{$title}}</div>
|
<div class="title">{{$title}}</div>
|
||||||
<div class="content">{{ $slot }}</div>
|
<div class="content">{{ $slot }}</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<input type="checkbox" class="switch" value="{{$value}}" {{$checked ? "checked" : ""}} />
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
@props([
|
||||||
|
'schema',
|
||||||
|
'createMode',
|
||||||
|
])
|
||||||
|
|
||||||
|
@php
|
||||||
|
$groups = ["Main",...$schema->groups];
|
||||||
|
if(!$createMode){
|
||||||
|
$groups[] = "Backlinks";
|
||||||
|
}
|
||||||
|
|
||||||
|
@endphp
|
||||||
|
<ul class="tabs">
|
||||||
|
@foreach($groups as $tab)
|
||||||
|
<li class="tab">
|
||||||
|
<button class="button" aria-current="page">
|
||||||
|
{{$tab}}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
@endforeach
|
||||||
|
</ul>
|
||||||
|
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||||
<title>@yield('title') - Lucent Data Platform</title>
|
<title>{{$title}} - Lucent Data Platform</title>
|
||||||
@if(config("lucent.env") == "production")
|
@if(config("lucent.env") == "production")
|
||||||
<!-- if production -->
|
<!-- if production -->
|
||||||
<link rel="stylesheet" href="/vendor/lucent/dist/{{ $manifest['main.js']["css"][0] }}"/>
|
<link rel="stylesheet" href="/vendor/lucent/dist/{{ $manifest['main.js']["css"][0] }}"/>
|
||||||
@@ -24,7 +24,6 @@
|
|||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
|
||||||
@yield('content')
|
@yield('content')
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<dialog id="dialog-{{$schema->name}}">
|
||||||
|
|
||||||
|
|
||||||
|
@if($schema)
|
||||||
|
<div class="dialog-header">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="button"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
Insert
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="button"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
Replace
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="hide">
|
||||||
|
<span class="number-of-records-selected"></span> records selected
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="button close"
|
||||||
|
aria-label="Close"
|
||||||
|
>
|
||||||
|
<x-lucent::icon icon="close">
|
||||||
|
|
||||||
|
</x-lucent::icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="dialog-body">
|
||||||
|
@include("lucent::records.index")
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@endif
|
||||||
|
</dialog>
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
@extends("lucent::layouts.channel")
|
||||||
|
|
||||||
|
@section("content")
|
||||||
|
@php
|
||||||
|
$createMode = $createMode ?? false;
|
||||||
|
@endphp
|
||||||
|
<div class="record-edit">
|
||||||
|
@include("lucent::records-editor.header")
|
||||||
|
@include("lucent::records-editor.title")
|
||||||
|
<x-lucent::notice title="Submission Errors">
|
||||||
|
asfasf
|
||||||
|
</x-lucent::notice>
|
||||||
|
<div class=" mt-4" style="margin-bottom:150px;position:relative;">
|
||||||
|
<x-lucent::tabs :schema="$schema" :createMode="$createMode"></x-lucent::tabs>
|
||||||
|
</div>
|
||||||
|
<form id="record-form" data-record-id="{{$record->id}}">
|
||||||
|
@foreach($schema->fields as $field)
|
||||||
|
@include("lucent::records-editor.fields", ["field" => $field])
|
||||||
|
@endforeach
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@endsection
|
||||||
|
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<div class="field-header">
|
||||||
|
<div class="labels">
|
||||||
|
<div class="label-and-help">
|
||||||
|
<label for={{$id}}
|
||||||
|
>{{$field->label}}</label
|
||||||
|
>
|
||||||
|
@if(!empty($field->help))
|
||||||
|
<small class="help-text light-text">{{$field->help}}</small>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
tabindex="-1"
|
||||||
|
class="text-decoration-none"
|
||||||
|
><code class="field-id">{{$field->name}}</code>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
@php
|
||||||
|
$fieldId = "field-".$field->name."-".$record->id;
|
||||||
|
$params = [
|
||||||
|
"id" => $fieldId,
|
||||||
|
"value" => data_get($record->data,$field->name),
|
||||||
|
"errorMessage" => ""
|
||||||
|
];
|
||||||
|
@endphp
|
||||||
|
<div class="editor-field">
|
||||||
|
@include("lucent::records-editor.fieldHeader", $params)
|
||||||
|
@if($field->info->name === "text")
|
||||||
|
@include("lucent::records-editor.fields.text", $params)
|
||||||
|
@elseif($field->info->name === "slug")
|
||||||
|
@include("lucent::records-editor.fields.slug", $params)
|
||||||
|
@elseif($field->info->name === "color")
|
||||||
|
@include("lucent::records-editor.fields.color", $params)
|
||||||
|
@elseif($field->info->name === "file")
|
||||||
|
@include("lucent::records-editor.fields.file", $params)
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<div>
|
||||||
|
<div style="display: flex; align-items: center;gap: 10px" class="color-picker">
|
||||||
|
<input
|
||||||
|
type="color"
|
||||||
|
id="{{$id}}-picker"
|
||||||
|
style="border: none;background: transparent;padding: 0;width:64px;"
|
||||||
|
{{$field->readonly && !$createMode ? "disabled" : ""}}
|
||||||
|
value="{{$value}}"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="{{$id}}"
|
||||||
|
value="{{$value}}"
|
||||||
|
class="form-control {{!empty($errorMessage) ? "is-invalid" : "" }}"
|
||||||
|
autocomplete="off"
|
||||||
|
{{$field->readonly && !$createMode ? "readonly" : ""}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if($errorMessage)
|
||||||
|
<div class="invalid-feedback d-block">
|
||||||
|
{{$errorMessage}}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
@php
|
||||||
|
// $references = $graph->edges
|
||||||
|
// ->filter(fn($edge) => $edge->field === $field->name && $edge->source === $record->id)
|
||||||
|
// ->map(fn($edge) => $graph->records->firstWhere("id", $edge->target));
|
||||||
|
//
|
||||||
|
$references = collect([]);
|
||||||
|
|
||||||
|
$collectionSchemas = $schemas->whereIn("name",$field->collections);
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
@if(count($field->collections) === 1)
|
||||||
|
<button class="button" data-open-modal="{{$field->collections[0]}}">Browse</button>
|
||||||
|
@else
|
||||||
|
<x-lucent::dropdown>
|
||||||
|
Browse
|
||||||
|
<x-slot:items>
|
||||||
|
@foreach($collectionSchemas as $collectionSchema)
|
||||||
|
<a class="dropdown-item" data-open-modal="{{$collectionSchema->name}}" href="/">{{$collectionSchema->label}}</a>
|
||||||
|
@endforeach
|
||||||
|
</x-slot:items>
|
||||||
|
</x-lucent::dropdown>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if ($references->isNotEmpty())
|
||||||
|
<div class="sortable-container mt-3">
|
||||||
|
@foreach($references as $reference)
|
||||||
|
<!--This div helps the sorting thing-->
|
||||||
|
<div>
|
||||||
|
@include("lucent::records-editor.fields.file.preview", ["record" => $reference])
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
@php
|
||||||
|
$reference = $record;
|
||||||
|
$schema = $channel->schemas->firstWhere("name",$record->schema);
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
<div class="preview-file">
|
||||||
|
<div style="display: flex;align-items: center;gap: 10px;">
|
||||||
|
<div class="image">
|
||||||
|
@include("lucent::records-editor.fields.file.thumb", ["size" => "small"])
|
||||||
|
</div>
|
||||||
|
<div class="title">
|
||||||
|
<div>
|
||||||
|
<a class="record-title" href="{{lucent_url("records")}}/{{$record->id}}">
|
||||||
|
{{$viewModel->getRecordName($record)}}
|
||||||
|
</a>
|
||||||
|
<small class="d-block">
|
||||||
|
from {{$schema->label}}
|
||||||
|
@if ($record->status === "draft")
|
||||||
|
@include("lucent::records-editor.status", ["status" => $record->status])
|
||||||
|
@endif
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{-- <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"/>--}}
|
||||||
|
{{-- </div>--}}
|
||||||
|
{{-- <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>--}}
|
||||||
|
{{-- {/each}--}}
|
||||||
|
{{-- </Dropdown>--}}
|
||||||
|
|
||||||
|
{{-- </div>--}}
|
||||||
|
{{-- {/if}--}}
|
||||||
|
{{-- {#if hasDelete}--}}
|
||||||
|
{{-- <div class="reference-action">--}}
|
||||||
|
{{-- <button--}}
|
||||||
|
{{-- class="button"--}}
|
||||||
|
{{-- on:click={remove}--}}
|
||||||
|
{{-- >--}}
|
||||||
|
{{-- <Icon icon="trash-can"/>--}}
|
||||||
|
{{-- </button>--}}
|
||||||
|
{{-- </div>--}}
|
||||||
|
{{-- {/if}--}}
|
||||||
|
{{-- </div>--}}
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
@php
|
||||||
|
$imageSide = 256;
|
||||||
|
$fileSide = 32;
|
||||||
|
$fontSize = "20";
|
||||||
|
$showFilename = $showFilename ?? false;
|
||||||
|
|
||||||
|
|
||||||
|
if ($size === "medium") {
|
||||||
|
$imageSide = 128;
|
||||||
|
$fileSide = 12;
|
||||||
|
$fontSize = "17";
|
||||||
|
} else if ($size === "small") {
|
||||||
|
$imageSide = 64;
|
||||||
|
$fileSide = 12;
|
||||||
|
$fontSize = "15";
|
||||||
|
} else if ($size === "tiny") {
|
||||||
|
$imageSide = 42;
|
||||||
|
$fileSide = 12;
|
||||||
|
$fontSize = "13";
|
||||||
|
}
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
<div style="display: flex;align-items: center;gap: 5px;">
|
||||||
|
|
||||||
|
@if(str_starts_with($record->_file->mime, "image"))
|
||||||
|
<a
|
||||||
|
href="{{lucent_url("records")}}/{{$record->id}}"
|
||||||
|
title="{{$record->_file->originalName}}"
|
||||||
|
style="width:{{$imageSide}}px;height:{{$imageSide}}px"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
class="rounded w-100"
|
||||||
|
style="border-radius: 12px;padding: 4px;"
|
||||||
|
src={{lucent_thumbnail($record)}}
|
||||||
|
alt="{{$record->_file->path}}"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
@else
|
||||||
|
<a
|
||||||
|
href="{{lucent_url("records")}}/{{$record->id}}"
|
||||||
|
title="{{$record->_file->path}}"
|
||||||
|
class="file-preview-small"
|
||||||
|
style="width:{{$imageSide}}px;height:{{$imageSide}}px"
|
||||||
|
>
|
||||||
|
<x-lucent::icon icon="file" :width="$fileSide" :height="$fileSide"></x-lucent::icon>
|
||||||
|
<span class="ms-2"
|
||||||
|
>.{{pathinfo($record->_file->path, PATHINFO_EXTENSION)}}</span
|
||||||
|
>
|
||||||
|
</a>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if ($showFilename)
|
||||||
|
<a
|
||||||
|
href="{{lucent_url("records")}}/{{$record->id}}"
|
||||||
|
title="{{$record->_file->path}}"
|
||||||
|
class="preview-file-filename lx-small-text text-decoration-none"
|
||||||
|
>{{$record->_file->path}} </a>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<div>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="{{$id}}"
|
||||||
|
value="{{$value}}"
|
||||||
|
class="form-control {{!empty($errorMessage) ? "is-invalid" : "" }}"
|
||||||
|
autocomplete="off"
|
||||||
|
{{$field->readonly && !$createMode ? "readonly" : ""}}
|
||||||
|
/>
|
||||||
|
<div class="system-help-text light-text">
|
||||||
|
Leave this empty to autogenerate from <i>{{$field->source}}</i>
|
||||||
|
</div>
|
||||||
|
@if($errorMessage)
|
||||||
|
<div class="invalid-feedback d-block">
|
||||||
|
{{$errorMessage}}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<div style="position: relative;">
|
||||||
|
@if($field->selectOptions)
|
||||||
|
<Autocomplete {field} bind:value={value}></Autocomplete>
|
||||||
|
@else
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="{{$field->name}}"
|
||||||
|
id="{{$id}}"
|
||||||
|
value="{{$value}}"
|
||||||
|
class="form-control {{!empty($errorMessage) ? "is-invalid" : "" }}"
|
||||||
|
autocomplete="off"
|
||||||
|
{{$field->readonly && !$createMode ? "readonly" : ""}}
|
||||||
|
/>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if($errorMessage)
|
||||||
|
<div class="invalid-feedback d-block">
|
||||||
|
{{$errorMessage}}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
<div class="tools-header">
|
||||||
|
<div style="display: flex;align-items: center; gap:10px;">
|
||||||
|
@if(!$createMode)
|
||||||
|
<x-lucent::dropdown>
|
||||||
|
<x-lucent::icon icon="ellipsis"></x-lucent::icon>
|
||||||
|
<x-slot:items>
|
||||||
|
<h6 class="dropdown-header">Record Actions</h6>
|
||||||
|
<a
|
||||||
|
class="dropdown-item"
|
||||||
|
href="{{lucent_url("records/new")}}?schema={{$schema->name}}"
|
||||||
|
>
|
||||||
|
Create new
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
class="dropdown-item"
|
||||||
|
href="{{lucent_url("records/clone")}}/{{$record->id}}"
|
||||||
|
>
|
||||||
|
Clone
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
class="dropdown-item"
|
||||||
|
href="{{lucent_url("records/revisions")}}/{{$record->id}}">Revisions</a
|
||||||
|
>
|
||||||
|
</x-slot>
|
||||||
|
</x-lucent::dropdown>
|
||||||
|
@endif
|
||||||
|
@if($record->status !== "trashed")
|
||||||
|
<x-lucent::switch value="published" :checked="$record->status === 'published'"></x-lucent::switch>
|
||||||
|
@endif
|
||||||
|
@if($record->status === "published")
|
||||||
|
Published
|
||||||
|
@elseif($record->status === "draft")
|
||||||
|
Draft
|
||||||
|
@elseif($record->status === "trashed")
|
||||||
|
Trashed
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
@if($createMode)
|
||||||
|
<button
|
||||||
|
id="record-create-button"
|
||||||
|
class="button primary btn-spinner"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="spinner-border spinner-border-sm"
|
||||||
|
role="status"
|
||||||
|
aria-hidden="true"
|
||||||
|
></span>
|
||||||
|
Create
|
||||||
|
</button>
|
||||||
|
@else
|
||||||
|
<button
|
||||||
|
id="record-save-button"
|
||||||
|
type="button"
|
||||||
|
class="button primary ms-2 btn btn-primary btn-spinner"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="spinner-border spinner-border-sm"
|
||||||
|
role="status"
|
||||||
|
aria-hidden="true"
|
||||||
|
></span>
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
@php
|
||||||
|
|
||||||
|
$statusList = [
|
||||||
|
"published" => [
|
||||||
|
"value" => "published",
|
||||||
|
"text" => "Published",
|
||||||
|
"bg" => "success",
|
||||||
|
"color" => "white",
|
||||||
|
],
|
||||||
|
"trashed" => [
|
||||||
|
"value" => "trashed",
|
||||||
|
"text" => "Trashed",
|
||||||
|
"bg" => "danger",
|
||||||
|
"color" => "white",
|
||||||
|
],
|
||||||
|
"draft" => [
|
||||||
|
"value" => "draft",
|
||||||
|
"text" => "Draft",
|
||||||
|
"bg" => "warning",
|
||||||
|
"color" => "dark",
|
||||||
|
],
|
||||||
|
];
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
<span class="badge text-bg-{{$statusList[$status->value]["bg"]}}" style="max-width:84px"
|
||||||
|
>{{$statusList[$status->value]["text"]}}</span
|
||||||
|
>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<div class="record-header">
|
||||||
|
<a class="schema-name" href="{{lucent_url("content")}}/{{$schema->name}}">{{strtoupper($schema->label)}}</a>
|
||||||
|
<span class="record-title">
|
||||||
|
@if(!$createMode)
|
||||||
|
{{$viewModel->getRecordName($record)}}
|
||||||
|
@else
|
||||||
|
New Record
|
||||||
|
@endif
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
|
||||||
|
<div class="">
|
||||||
|
<div class="{{$inModal ? 'mt-0' : 'mt-5'}}">
|
||||||
|
<h3 class="header-normal mb-5 ">
|
||||||
|
{{$schema->label}}
|
||||||
|
</h3>
|
||||||
|
{{-- {#if selected.length > 0 && !inModal && isWritable}--}}
|
||||||
|
{{-- <ActionsOnSelected {schema} {selected} {filter}/>--}}
|
||||||
|
{{-- {:else}--}}
|
||||||
|
{{-- <Tools--}}
|
||||||
|
{{-- bind:schema--}}
|
||||||
|
{{-- bind:records--}}
|
||||||
|
{{-- {systemFields}--}}
|
||||||
|
{{-- {sortParam}--}}
|
||||||
|
{{-- {sortField}--}}
|
||||||
|
{{-- {operators}--}}
|
||||||
|
{{-- {filter}--}}
|
||||||
|
{{-- {graph}--}}
|
||||||
|
{{-- {inModal}--}}
|
||||||
|
{{-- {modalUrl}--}}
|
||||||
|
{{-- {isWritable}--}}
|
||||||
|
{{-- on:refresh={refresh}--}}
|
||||||
|
{{-- />--}}
|
||||||
|
{{-- {/if}--}}
|
||||||
|
@include("lucent::records.tools")
|
||||||
|
@include("lucent::records.table")
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- <Pagination--}}
|
||||||
|
{{-- {limit}--}}
|
||||||
|
{{-- {skip}--}}
|
||||||
|
{{-- {total}--}}
|
||||||
|
{{-- on:refresh={refresh}--}}
|
||||||
|
{{-- {inModal}--}}
|
||||||
|
{{-- {modalUrl}--}}
|
||||||
|
{{-- />--}}
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
@extends("lucent::layouts.channel")
|
||||||
|
|
||||||
|
@section("content")
|
||||||
|
@include("lucent::records.index")
|
||||||
|
@endsection
|
||||||
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
@foreach($schema->visible as $visibleColumn)
|
||||||
|
@php
|
||||||
|
$schemaField = $schema->fields->firstWhere("name", $visibleColumn);
|
||||||
|
@endphp
|
||||||
|
<td class="field-ui-{{$schemaField->info->name ?? $visibleColumn}} {{$visibleColumn === $sortField->name ? "is-sort" : ""}}">
|
||||||
|
@if(in_array($visibleColumn ,["_sys.createdBy","_sys.updatedBy"]))
|
||||||
|
<x-lucent::avatar side="24" :user="$users->firstWhere('id',$record->_sys->createdBy)"></x-lucent::avatar>
|
||||||
|
@elseif($visibleColumn === "_sys.status")
|
||||||
|
@include("lucent::records-editor.status",[ "status" => $record->status])
|
||||||
|
@else
|
||||||
|
{!! $viewModel->renderRow($record,$schemaField)!!}
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
@endforeach
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
<div class="table mt-5 ">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
@if($isWritable)
|
||||||
|
<th>
|
||||||
|
<x-lucent::checkbox value=""></x-lucent::checkbox>
|
||||||
|
</th>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@foreach($schema->visible as $visibleColumn)
|
||||||
|
@php
|
||||||
|
$schemaField = $schema->fields->firstWhere("name", $visibleColumn);
|
||||||
|
if(empty($schemaField)){
|
||||||
|
$schemaField = collect($systemFields)->firstWhere("name", str_replace("_sys.", "",$visibleColumn) );
|
||||||
|
}
|
||||||
|
@endphp
|
||||||
|
<th
|
||||||
|
class="field-ui-{{$schemaField->info->name ?? $schemaField->ui}} {{$schemaField->name === $sortField->name ? "is-sort" : ""}}"
|
||||||
|
scope="col"
|
||||||
|
title={{$schemaField->help ?? ""}}
|
||||||
|
>{{$schemaField->label}}</th
|
||||||
|
>
|
||||||
|
@endforeach
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach($records as $record)
|
||||||
|
<tr>
|
||||||
|
<td class="title-td">
|
||||||
|
<div
|
||||||
|
class="title-td-contents"
|
||||||
|
>
|
||||||
|
@if($isWritable)
|
||||||
|
<x-lucent::checkbox :value="$record->id"></x-lucent::checkbox>
|
||||||
|
|
||||||
|
@endif
|
||||||
|
@if($record->_file?->path)
|
||||||
|
<div class="file-table-row">
|
||||||
|
@include("lucent::records-editor.fields.file.thumb", ["size" => "small"])
|
||||||
|
<div>
|
||||||
|
@if($record->status === "draft")
|
||||||
|
<span style="text-transform: uppercase;font-size:10px">{{$record->status}}</span>
|
||||||
|
@endif
|
||||||
|
<a
|
||||||
|
href="{{lucent_url("records")}}/{{$record->id}}"
|
||||||
|
target={{$inModal ? "_blank" : "_self"}}
|
||||||
|
>
|
||||||
|
{{ $viewModel->getRecordName($record)}}
|
||||||
|
</a>
|
||||||
|
<span>{{ (int)($record->_file->size / 1024) }}kB</span>
|
||||||
|
|
||||||
|
@if($record->_file->width > 0)
|
||||||
|
<span>{{$record->_file->width . "x" . $record->_file->height}}</span>
|
||||||
|
@endif
|
||||||
|
<a
|
||||||
|
href="{{lucent_file($record)}}"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Download
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
@else
|
||||||
|
<a
|
||||||
|
href="{{lucent_url("records")}}/{{$record->id}}"
|
||||||
|
target={{$inModal ? "_blank" : "_self"}}
|
||||||
|
>
|
||||||
|
@if($record->status === "draft")
|
||||||
|
<span style="text-transform: uppercase;font-size:10px">{{$record->status}}</span>
|
||||||
|
@endif
|
||||||
|
{{$viewModel->getRecordName($record)}}
|
||||||
|
</a>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
@include("lucent::records.row")
|
||||||
|
<td>
|
||||||
|
<x-lucent::avatar side="24"
|
||||||
|
:user="$users->firstWhere('id',$record->_sys->createdBy)"></x-lucent::avatar>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
<div class="toolbar">
|
||||||
|
<div class="toolbar-filters">
|
||||||
|
|
||||||
|
{{-- <SortFields--}}
|
||||||
|
{{-- {schema}--}}
|
||||||
|
{{-- {sortParam}--}}
|
||||||
|
{{-- {sortField}--}}
|
||||||
|
{{-- {systemFields}--}}
|
||||||
|
{{-- {inModal}--}}
|
||||||
|
{{-- {modalUrl}--}}
|
||||||
|
{{-- on:refresh--}}
|
||||||
|
{{-- />--}}
|
||||||
|
|
||||||
|
|
||||||
|
{{-- <FilterFields--}}
|
||||||
|
{{-- bind:schema--}}
|
||||||
|
{{-- {systemFields}--}}
|
||||||
|
{{-- {operators}--}}
|
||||||
|
{{-- {filter}--}}
|
||||||
|
{{-- {inModal}--}}
|
||||||
|
{{-- {modalUrl}--}}
|
||||||
|
{{-- on:refresh--}}
|
||||||
|
{{-- />--}}
|
||||||
|
|
||||||
|
<form method="GET">
|
||||||
|
<input type="search" name="filter[search_regex]" placeholder="Search"
|
||||||
|
class="search" required>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="display:flex;align-items: center;gap:4px">
|
||||||
|
@if(get_class($schema) === \Lucent\Schema\CollectionSchema::class)
|
||||||
|
@if(!$inModal && $isWritable)
|
||||||
|
<a
|
||||||
|
href="{{lucent_url("records/new?schema=")}}{{$schema->name}}"
|
||||||
|
class="button"
|
||||||
|
>
|
||||||
|
New Record
|
||||||
|
</a>
|
||||||
|
@endif
|
||||||
|
@else
|
||||||
|
<div>
|
||||||
|
{{-- <Uploader {schema} on:uploadComplete={uploadComplete}/>--}}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
@if(!$inModal)
|
||||||
|
{{-- <Dropdown orientation="right">--}}
|
||||||
|
{{-- <div slot="button">--}}
|
||||||
|
{{-- <Icon icon="ellipsis-vertical"/>--}}
|
||||||
|
{{-- </div>--}}
|
||||||
|
{{-- {#if filter["status_in"] === "trashed"}--}}
|
||||||
|
{{-- {#if isWritable}--}}
|
||||||
|
{{-- <a--}}
|
||||||
|
{{-- class="dropdown-item"--}}
|
||||||
|
{{-- href="{channel.lucentUrl}/content/{schema.name}/emptyTrash"--}}
|
||||||
|
{{-- >--}}
|
||||||
|
{{-- Empty trash--}}
|
||||||
|
{{-- </a>--}}
|
||||||
|
{{-- {/if}--}}
|
||||||
|
{{-- {:else}--}}
|
||||||
|
|
||||||
|
{{-- <a--}}
|
||||||
|
{{-- class="dropdown-item"--}}
|
||||||
|
{{-- href={csvUrl}--}}
|
||||||
|
{{-- >Export to CSV</a--}}
|
||||||
|
{{-- >--}}
|
||||||
|
{{-- <a--}}
|
||||||
|
{{-- class="dropdown-item"--}}
|
||||||
|
{{-- href="{channel.lucentUrl}/content/{schema.name}?filter[status_in]=trashed"--}}
|
||||||
|
{{-- >View trashed records</a--}}
|
||||||
|
{{-- >--}}
|
||||||
|
{{-- <a--}}
|
||||||
|
{{-- class="dropdown-item"--}}
|
||||||
|
{{-- href="{channel.lucentUrl}/content/{schema.name}?notlinked=*"--}}
|
||||||
|
{{-- >View unlinked records</a--}}
|
||||||
|
{{-- >--}}
|
||||||
|
{{-- {/if}--}}
|
||||||
|
{{-- </Dropdown>--}}
|
||||||
|
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{{--<div class="applied-filters">--}}
|
||||||
|
{{-- <AppliedFilterNotLinked--}}
|
||||||
|
{{-- {inModal}--}}
|
||||||
|
{{-- {modalUrl}--}}
|
||||||
|
{{-- on:refresh--}}
|
||||||
|
{{-- ></AppliedFilterNotLinked>--}}
|
||||||
|
{{-- {#if Object.entries(filter).length > 0}--}}
|
||||||
|
{{-- {#each Object.entries(filter) as [k, v]}--}}
|
||||||
|
{{-- <AppliedFilter--}}
|
||||||
|
{{-- {schema}--}}
|
||||||
|
{{-- {operators}--}}
|
||||||
|
{{-- key={k}--}}
|
||||||
|
{{-- value={v}--}}
|
||||||
|
{{-- {inModal}--}}
|
||||||
|
{{-- {modalUrl}--}}
|
||||||
|
{{-- {graph}--}}
|
||||||
|
{{-- on:refresh--}}
|
||||||
|
{{-- />--}}
|
||||||
|
{{-- {/each}--}}
|
||||||
|
{{-- {/if}--}}
|
||||||
|
{{--</div>--}}
|
||||||
|
|
||||||
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<title>{{$title}}</title>
|
|
||||||
<meta http-equiv="refresh" content="0; url='{{$to}}'"/>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<p>{{$message}}</p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
+11
-11
@@ -3,7 +3,7 @@
|
|||||||
namespace Lucent\Account;
|
namespace Lucent\Account;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Lucent\Database\Database;
|
||||||
use Lucent\Primitive\Collection;
|
use Lucent\Primitive\Collection;
|
||||||
use PhpOption\Option;
|
use PhpOption\Option;
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ class UserRepo
|
|||||||
|
|
||||||
public function count(): int
|
public function count(): int
|
||||||
{
|
{
|
||||||
return DB::table("users")->count();
|
return Database::make()->table("users")->count();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -20,7 +20,7 @@ class UserRepo
|
|||||||
*/
|
*/
|
||||||
public function all(): Collection
|
public function all(): Collection
|
||||||
{
|
{
|
||||||
$usersData = DB::table("users")->get();
|
$usersData = Database::make()->table("users")->get();
|
||||||
|
|
||||||
$users = array_map(fn($userData) => $this->fromArray((array)$userData), $usersData->toArray());
|
$users = array_map(fn($userData) => $this->fromArray((array)$userData), $usersData->toArray());
|
||||||
return new Collection($users);
|
return new Collection($users);
|
||||||
@@ -31,14 +31,14 @@ class UserRepo
|
|||||||
{
|
{
|
||||||
$userData = toArray($user);
|
$userData = toArray($user);
|
||||||
$userData["roles"] = json_encode($userData["roles"]);
|
$userData["roles"] = json_encode($userData["roles"]);
|
||||||
DB::table("users")->insert($userData);
|
Database::make()->table("users")->insert($userData);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(User $user): void
|
public function update(User $user): void
|
||||||
{
|
{
|
||||||
$userData = toArray($user);
|
$userData = toArray($user);
|
||||||
$userData["roles"] = json_encode($userData["roles"]);
|
$userData["roles"] = json_encode($userData["roles"]);
|
||||||
DB::table("users")->where("id", $user->id)->update($userData);
|
Database::make()->table("users")->where("id", $user->id)->update($userData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ class UserRepo
|
|||||||
{
|
{
|
||||||
$newToken = Token::new(32);
|
$newToken = Token::new(32);
|
||||||
|
|
||||||
DB::table("users")
|
Database::make()->table("users")
|
||||||
->where("id", $id)
|
->where("id", $id)
|
||||||
->update([
|
->update([
|
||||||
'loggedInAt' => Carbon::now()->toJson(),
|
'loggedInAt' => Carbon::now()->toJson(),
|
||||||
@@ -62,7 +62,7 @@ class UserRepo
|
|||||||
*/
|
*/
|
||||||
public function findByEmail(Email $email): Option
|
public function findByEmail(Email $email): Option
|
||||||
{
|
{
|
||||||
$user = DB::table("users")->where("email", $email->value())->first();
|
$user = Database::make()->table("users")->where("email", $email->value())->first();
|
||||||
|
|
||||||
if (empty($user)) {
|
if (empty($user)) {
|
||||||
return none();
|
return none();
|
||||||
@@ -76,7 +76,7 @@ class UserRepo
|
|||||||
*/
|
*/
|
||||||
public function findById(string $id): Option
|
public function findById(string $id): Option
|
||||||
{
|
{
|
||||||
$user = DB::table("users")->where("id", $id)->first();
|
$user = Database::make()->table("users")->where("id", $id)->first();
|
||||||
|
|
||||||
if (empty($user)) {
|
if (empty($user)) {
|
||||||
return none();
|
return none();
|
||||||
@@ -88,12 +88,12 @@ class UserRepo
|
|||||||
|
|
||||||
public function updateName(string $userId, Name $name): void
|
public function updateName(string $userId, Name $name): void
|
||||||
{
|
{
|
||||||
DB::table("users")->where("id", $userId)->update(["name" => $name->value]);
|
Database::make()->table("users")->where("id", $userId)->update(["name" => $name->value]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function updateEmail(string $userId, Email $email): void
|
public function updateEmail(string $userId, Email $email): void
|
||||||
{
|
{
|
||||||
DB::table("users")->where("id", $userId)->update(["email" => $email->value()]);
|
Database::make()->table("users")->where("id", $userId)->update(["email" => $email->value()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function fromArray(array $data): User
|
public function fromArray(array $data): User
|
||||||
@@ -102,7 +102,7 @@ class UserRepo
|
|||||||
id: $data["id"],
|
id: $data["id"],
|
||||||
name: new Name($data["name"] ?? ""),
|
name: new Name($data["name"] ?? ""),
|
||||||
email: new Email($data["email"]),
|
email: new Email($data["email"]),
|
||||||
roles: json_decode($data["roles"] ?? "[]",true),
|
roles: json_decode($data["roles"] ?? "[]", true),
|
||||||
createdAt: $data["createdAt"],
|
createdAt: $data["createdAt"],
|
||||||
updatedAt: $data["updatedAt"],
|
updatedAt: $data["updatedAt"],
|
||||||
loggedInAt: $data["loggedInAt"] ?? null,
|
loggedInAt: $data["loggedInAt"] ?? null,
|
||||||
|
|||||||
+26
-3
@@ -2,31 +2,54 @@
|
|||||||
|
|
||||||
namespace Lucent\Channel;
|
namespace Lucent\Channel;
|
||||||
|
|
||||||
|
use Lucent\Channel\Data\UserCommand;
|
||||||
use Lucent\Primitive\Collection;
|
use Lucent\Primitive\Collection;
|
||||||
|
use Lucent\Schema\FilesSchema;
|
||||||
use Lucent\Schema\Schema;
|
use Lucent\Schema\Schema;
|
||||||
|
|
||||||
final class Channel
|
final class Channel
|
||||||
{
|
{
|
||||||
public string $lucentUrl;
|
public string $lucentUrl;
|
||||||
public string $filesUrl;
|
public string $filesUrl;
|
||||||
|
public array $disks;
|
||||||
public string $previewTargetUrl;
|
public string $previewTargetUrl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Collection<Schema> $schemas
|
* @param Collection<Schema> $schemas
|
||||||
|
* @param Collection<UserCommand> $commands
|
||||||
*/
|
*/
|
||||||
function __construct(
|
function __construct(
|
||||||
public string $name,
|
public string $name,
|
||||||
public string $url,
|
public string $url,
|
||||||
public string $previewTarget,
|
public string $previewTarget,
|
||||||
public string $generateCommand,
|
public Collection $commands,
|
||||||
public Collection $schemas,
|
public Collection $schemas,
|
||||||
public array $imageFilters,
|
public array $imageFilters,
|
||||||
public array $roles,
|
public array $roles,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
$this->lucentUrl = $url . "/lucent";
|
$this->lucentUrl = $url . "/lucent";
|
||||||
$this->filesUrl = $url . "/storage";
|
$this->filesUrl = $this->makeFilesUrl();
|
||||||
$this->previewTargetUrl = $url . "/". $previewTarget;
|
$this->disks = $this->getDisksFromSchemas();
|
||||||
|
$this->previewTargetUrl = $url . "/" . $previewTarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private function makeFilesUrl(): string
|
||||||
|
{
|
||||||
|
return match (config("filesystems.disks.lucent.driver")) {
|
||||||
|
"s3" => config("filesystems.disks.lucent.endpoint") . "/" . config("filesystems.disks.lucent.bucket"),
|
||||||
|
"local" => $this->url . "/storage" . config("filesystems.disks.lucent.endpoint"),
|
||||||
|
default => ""
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getDisksFromSchemas()
|
||||||
|
{
|
||||||
|
return $this->schemas->filter(fn(Schema $schema) => get_class($schema) === FilesSchema::class)->reduce(function (array $carry, Schema $schema) {
|
||||||
|
$carry[$schema->disk] = config("filesystems.disks." . $schema->disk . ".url");
|
||||||
|
return $carry;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace Lucent\Channel;
|
namespace Lucent\Channel;
|
||||||
|
|
||||||
|
|
||||||
|
use Lucent\Channel\Data\UserCommand;
|
||||||
use Lucent\Primitive\Collection;
|
use Lucent\Primitive\Collection;
|
||||||
use Lucent\Schema\Schema;
|
use Lucent\Schema\Schema;
|
||||||
use Lucent\Schema\SchemaService;
|
use Lucent\Schema\SchemaService;
|
||||||
@@ -13,7 +14,7 @@ final class ChannelService
|
|||||||
public Channel $channel;
|
public Channel $channel;
|
||||||
|
|
||||||
private function __construct(
|
private function __construct(
|
||||||
public SchemaService $schemaService
|
public SchemaService $schemaService,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -29,11 +30,16 @@ final class ChannelService
|
|||||||
$schemaService = new SchemaService();
|
$schemaService = new SchemaService();
|
||||||
$schemasCollection = (new Collection($schemasArray["schemas"] ?? []))->map([$schemaService, 'fromArray']);
|
$schemasCollection = (new Collection($schemasArray["schemas"] ?? []))->map([$schemaService, 'fromArray']);
|
||||||
|
|
||||||
|
$userCommands = [];
|
||||||
|
foreach (config("lucent.commands") ?? [] as $signature => $desc) {
|
||||||
|
$userCommands[] = new UserCommand($desc, $signature);
|
||||||
|
}
|
||||||
|
|
||||||
$channel = new Channel(
|
$channel = new Channel(
|
||||||
name: config("lucent.name") ?? "",
|
name: config("lucent.name") ?? "",
|
||||||
url: rtrim(config("lucent.url") ?? "", "/"),
|
url: rtrim(config("lucent.url") ?? "", "/"),
|
||||||
previewTarget: rtrim(config("lucent.previewTarget") ?? "", "/"),
|
previewTarget: rtrim(config("lucent.previewTarget") ?? "", "/"),
|
||||||
generateCommand: config("lucent.generateCommand") ?? "",
|
commands: Collection::make($userCommands),
|
||||||
schemas: $schemasCollection,
|
schemas: $schemasCollection,
|
||||||
imageFilters: config("lucent.imageFilters") ?? [],
|
imageFilters: config("lucent.imageFilters") ?? [],
|
||||||
roles: $schemasArray["roles"] ?? []
|
roles: $schemasArray["roles"] ?? []
|
||||||
@@ -63,7 +69,7 @@ final class ChannelService
|
|||||||
*/
|
*/
|
||||||
public function schemasReadableByRoles(array $roles): array
|
public function schemasReadableByRoles(array $roles): array
|
||||||
{
|
{
|
||||||
$schemasAllRead = $this->channel->schemas->filter(fn(Schema $schema) => empty($schema->read))->values()->pluck("name");
|
$schemasAllRead = $this->channel->schemas->filter(fn(Schema $schema) => empty($schema->read))->values()->pluck("name");
|
||||||
$schemasCanRead = $this->channel->schemas->filter(fn(Schema $schema) => count(array_intersect($schema->read ?? [], $roles)) > 0)->values()->pluck("name");
|
$schemasCanRead = $this->channel->schemas->filter(fn(Schema $schema) => count(array_intersect($schema->read ?? [], $roles)) > 0)->values()->pluck("name");
|
||||||
$schemasCanWrite = $this->channel->schemas->filter(fn(Schema $schema) => count(array_intersect($schema->write ?? [], $roles)) > 0)->values()->pluck("name");
|
$schemasCanWrite = $this->channel->schemas->filter(fn(Schema $schema) => count(array_intersect($schema->write ?? [], $roles)) > 0)->values()->pluck("name");
|
||||||
return $schemasAllRead->merge($schemasCanRead)->merge($schemasCanWrite)->unique()->values()->toArray();
|
return $schemasAllRead->merge($schemasCanRead)->merge($schemasCanWrite)->unique()->values()->toArray();
|
||||||
@@ -76,8 +82,8 @@ final class ChannelService
|
|||||||
*/
|
*/
|
||||||
public function schemasWritableByRoles(array $roles): array
|
public function schemasWritableByRoles(array $roles): array
|
||||||
{
|
{
|
||||||
$schemasAllRead = $this->channel->schemas->filter(fn(Schema $schema) => empty($schema->write ?? []))->values()->pluck("name");
|
$schemasAllRead = $this->channel->schemas->filter(fn(Schema $schema) => empty($schema->write ?? []))->values()->pluck("name");
|
||||||
$schemasCanWrite = $this->channel->schemas->filter(fn(Schema $schema) => count(array_intersect($schema->write ?? [], $roles)) > 0)->values()->pluck("name");
|
$schemasCanWrite = $this->channel->schemas->filter(fn(Schema $schema) => count(array_intersect($schema->write ?? [], $roles)) > 0)->values()->pluck("name");
|
||||||
return $schemasAllRead->merge($schemasCanWrite)->unique()->values()->toArray();
|
return $schemasAllRead->merge($schemasCanWrite)->unique()->values()->toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lucent\Channel\Data;
|
||||||
|
|
||||||
|
class UserCommand
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public string $name,
|
||||||
|
public string $signature,
|
||||||
|
)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lucent\Command;
|
||||||
|
|
||||||
|
use Lucent\Command\Data\CommandLogItem;
|
||||||
|
use Lucent\Database\Database;
|
||||||
|
|
||||||
|
class CommandRepo
|
||||||
|
{
|
||||||
|
public function findBySignature($signature): ?CommandLogItem
|
||||||
|
{
|
||||||
|
|
||||||
|
$row = Database::make()->table("command_logs")->where("signature", $signature)->first();
|
||||||
|
if (empty($row)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return CommandLogItem::fromDB($row);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function upsertCommand(CommandLogItem $commandLogItem): void
|
||||||
|
{
|
||||||
|
$foundCommandLogItem = $this->findBySignature($commandLogItem->signature);
|
||||||
|
if (empty($foundCommandLogItem)) {
|
||||||
|
Database::make()->table("command_logs")->insert(toArray($commandLogItem));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Database::make()->table("command_logs")->where("signature", $commandLogItem->signature)->update(toArray($commandLogItem));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function appendToLogs(string $signature, string $line): void
|
||||||
|
{
|
||||||
|
Database::make()->update(
|
||||||
|
'update command_logs set logs = logs || ? where signature = ?',
|
||||||
|
[$line, $signature]
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lucent\Command;
|
||||||
|
|
||||||
|
use Lucent\Command\Data\CommandLogItem;
|
||||||
|
use Lucent\Id\Id;
|
||||||
|
use Lucent\LucentException;
|
||||||
|
use Symfony\Component\Process\PhpExecutableFinder;
|
||||||
|
|
||||||
|
|
||||||
|
class CommandService
|
||||||
|
{
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private CommandRepo $commandRepo,
|
||||||
|
)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function run(string $signature): CommandLogItem
|
||||||
|
{
|
||||||
|
$commandLogItem = $this->commandRepo->findBySignature($signature);
|
||||||
|
|
||||||
|
if (empty($commandLogItem)) {
|
||||||
|
$commandLogItem = new CommandLogItem(
|
||||||
|
id: Id::new(),
|
||||||
|
signature: $signature,
|
||||||
|
pid: null,
|
||||||
|
logs: ""
|
||||||
|
);
|
||||||
|
} elseif ($this->commandIsRunning($commandLogItem->pid)) {
|
||||||
|
throw new LucentException('Command is already running');
|
||||||
|
}
|
||||||
|
|
||||||
|
$commandLogItem->pid = $this->runCommand($signature);
|
||||||
|
$commandLogItem->logs = "";
|
||||||
|
$this->commandRepo->upsertCommand($commandLogItem);
|
||||||
|
return $commandLogItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function logWriter(string $signature): callable
|
||||||
|
{
|
||||||
|
return function (string $line) use($signature) {
|
||||||
|
$this->commandRepo->appendToLogs($signature, $line.PHP_EOL);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private function runCommand(string $signature): int
|
||||||
|
{
|
||||||
|
$phpBinaryFinder = new PhpExecutableFinder();
|
||||||
|
$phpBinaryPath = $phpBinaryFinder->find();
|
||||||
|
$pid = (int)shell_exec("cd " . base_path() . " && $phpBinaryPath artisan {$signature} > /dev/null 2>&1 & echo $!");
|
||||||
|
return $pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function commandIsRunning(int $pid): bool
|
||||||
|
{
|
||||||
|
return file_exists("/proc/$pid");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lucent\Command\Data;
|
||||||
|
|
||||||
|
use stdClass;
|
||||||
|
|
||||||
|
class CommandLogItem
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public string $id,
|
||||||
|
public string $signature,
|
||||||
|
public ?int $pid,
|
||||||
|
public string $logs,
|
||||||
|
)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function fromDB(stdClass $data): self
|
||||||
|
{
|
||||||
|
return new self(
|
||||||
|
id: $data->id,
|
||||||
|
signature: $data->signature,
|
||||||
|
pid: $data->pid,
|
||||||
|
logs: $data->logs,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,19 +16,13 @@ class CompileSchemas extends Command
|
|||||||
protected $description = 'Compiles schemas';
|
protected $description = 'Compiles schemas';
|
||||||
|
|
||||||
|
|
||||||
public function __construct(
|
public function handle(SchemaService $schemaService)
|
||||||
public SchemaService $schemaService
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
parent::__construct();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
$configDir = base_path(config('lucent.schemas_path'));
|
$configDir = base_path(config('lucent.schemas_path'));
|
||||||
$schemasDirIterator = new DirectoryIterator($configDir);
|
$schemasDirIterator = new DirectoryIterator($configDir);
|
||||||
$schemas = [];
|
$schemas = [];
|
||||||
|
|
||||||
foreach ($schemasDirIterator as $file) {
|
foreach ($schemasDirIterator as $file) {
|
||||||
if ($file->getExtension() !== "json") {
|
if ($file->getExtension() !== "json") {
|
||||||
continue;
|
continue;
|
||||||
@@ -46,7 +40,7 @@ class CompileSchemas extends Command
|
|||||||
|
|
||||||
$schemas = collect($schemas)->sortBy("label")->values();
|
$schemas = collect($schemas)->sortBy("label")->values();
|
||||||
$roles = $schemas
|
$roles = $schemas
|
||||||
->map([$this->schemaService, 'fromArray'])
|
->map([$schemaService, 'fromArray'])
|
||||||
->whereIn("type", [Type::COLLECTION, Type::FILES])
|
->whereIn("type", [Type::COLLECTION, Type::FILES])
|
||||||
->reduce(fn($carry, Schema $schema) => array_merge(
|
->reduce(fn($carry, Schema $schema) => array_merge(
|
||||||
$carry,
|
$carry,
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lucent\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Lucent\Primitive\Collection;
|
||||||
|
use Lucent\Schema\CollectionSchema;
|
||||||
|
|
||||||
|
class GenerateCollectionSchema extends Command
|
||||||
|
{
|
||||||
|
|
||||||
|
protected $signature = 'lucent:generate:collection {name}';
|
||||||
|
|
||||||
|
protected $description = 'Generate a lucent collection';
|
||||||
|
|
||||||
|
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$name = $this->argument('name');
|
||||||
|
$schema = new CollectionSchema(
|
||||||
|
name: $name,
|
||||||
|
label: $name,
|
||||||
|
visible: [],
|
||||||
|
groups: [],
|
||||||
|
fields: Collection::make(),
|
||||||
|
);
|
||||||
|
|
||||||
|
$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.");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
<?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,
|
||||||
|
visible: [],
|
||||||
|
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.");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Lucent\Commands;
|
|
||||||
|
|
||||||
use DirectoryIterator;
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
|
|
||||||
class LiveLink extends Command
|
|
||||||
{
|
|
||||||
|
|
||||||
protected $signature = 'lucent:livelink';
|
|
||||||
|
|
||||||
protected $description = 'Create live folder link';
|
|
||||||
|
|
||||||
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
symlink(storage_path("lucent/live"), public_path("live"));
|
|
||||||
$this->info("public link was created");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -7,6 +7,9 @@ use Exception;
|
|||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Intervention\Image\ImageManager;
|
use Intervention\Image\ImageManager;
|
||||||
use Lucent\Channel\ChannelService;
|
use Lucent\Channel\ChannelService;
|
||||||
|
use Lucent\File\FileService;
|
||||||
|
use Lucent\Query\Query;
|
||||||
|
use Lucent\Schema\FilesSchema;
|
||||||
use Lucent\Schema\Schema;
|
use Lucent\Schema\Schema;
|
||||||
use Lucent\Schema\Type;
|
use Lucent\Schema\Type;
|
||||||
|
|
||||||
@@ -18,7 +21,10 @@ class RebuildThumbnails extends Command
|
|||||||
protected $description = 'Rebuilds thumbnails for path';
|
protected $description = 'Rebuilds thumbnails for path';
|
||||||
|
|
||||||
|
|
||||||
public function __construct(public ImageManager $imageManager)
|
public function __construct(
|
||||||
|
public Query $query,
|
||||||
|
public FileService $fileService,
|
||||||
|
)
|
||||||
{
|
{
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
@@ -27,49 +33,19 @@ class RebuildThumbnails extends Command
|
|||||||
public function handle(ChannelService $channelService): int
|
public function handle(ChannelService $channelService): int
|
||||||
{
|
{
|
||||||
$channelService->channel->schemas
|
$channelService->channel->schemas
|
||||||
->where("type", Type::FILES)->values()
|
->filter(fn(Schema $schema) => get_class($schema) === FilesSchema::class)
|
||||||
->map([$this, 'rebuildThumbnails']);
|
->map([$this, 'rebuildThumbnails']);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function rebuildThumbnails(Schema $schema): void
|
public function rebuildThumbnails(FilesSchema $schema): void
|
||||||
{
|
{
|
||||||
|
$this->info("Rebuilding thumbnails for ". $schema->name);
|
||||||
$filesDir = storage_path("app/public/" . $schema->path . "/");
|
$records = $this->query->filter(["schema" => $schema->name])->run()->records;
|
||||||
$thumbDir = storage_path("app/public/thumbs/" . $schema->path . "/");
|
$disk = $this->fileService->loadDisk($schema->disk);
|
||||||
if (!file_exists($thumbDir)) {
|
foreach ($records as $record) {
|
||||||
make_dir_r($thumbDir);
|
$this->fileService->createTemplates($disk, $record->_file->path);
|
||||||
}
|
|
||||||
|
|
||||||
if (!file_exists($filesDir)) {
|
|
||||||
make_dir_r($filesDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
$filesDirIterator = new DirectoryIterator($filesDir);
|
|
||||||
|
|
||||||
foreach ($filesDirIterator as $file) {
|
|
||||||
|
|
||||||
if ($file->isDot()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$image = $this->imageManager->make($filesDir . $file->getFilename());
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$this->error($e->getMessage());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$image->fit(300, 300);
|
|
||||||
try {
|
|
||||||
$image->encode('webp', 75)->save($thumbDir . $file->getFilename());
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$this->error($e->getMessage());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->info($file->getFilename());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lucent\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Lucent\Database\Database;
|
||||||
|
|
||||||
|
class SetupDatabase extends Command
|
||||||
|
{
|
||||||
|
|
||||||
|
protected $signature = 'lucent:setup-db';
|
||||||
|
|
||||||
|
protected $description = 'Run to setup a new database';
|
||||||
|
|
||||||
|
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
|
||||||
|
$dbConnection = config("lucent.database");
|
||||||
|
$databasePath = config("database.connections.$dbConnection.database");
|
||||||
|
|
||||||
|
if(file_exists($databasePath)){
|
||||||
|
$this->error("Database already exists.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
touch($databasePath);
|
||||||
|
$this->tableUsers();
|
||||||
|
$this->tableRecords();
|
||||||
|
$this->tableRevisions();
|
||||||
|
$this->tableSessions();
|
||||||
|
$this->tableCommandLogs();
|
||||||
|
|
||||||
|
$this->info("Lucent Database Setup Completed");
|
||||||
|
}
|
||||||
|
|
||||||
|
private function tableUsers(): void
|
||||||
|
{
|
||||||
|
Database::make()->getSchemaBuilder()->create('users', function (Blueprint $table) {
|
||||||
|
$table->uuid("id")->primary();
|
||||||
|
$table->string('name')->nullable();
|
||||||
|
$table->string('email')->unique();
|
||||||
|
$table->jsonb('roles');
|
||||||
|
$table->string('createdAt');
|
||||||
|
$table->string('updatedAt');
|
||||||
|
$table->string('loggedInAt');
|
||||||
|
$table->string('mailToken')->nullable();
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private function tableSessions(): void
|
||||||
|
{
|
||||||
|
Database::make()->getSchemaBuilder()->create('sessions', function (Blueprint $table) {
|
||||||
|
$table->string('id')->primary();
|
||||||
|
$table->foreignId('user_id')->nullable()->index();
|
||||||
|
$table->string('ip_address', 45)->nullable();
|
||||||
|
$table->text('user_agent')->nullable();
|
||||||
|
$table->longText('payload');
|
||||||
|
$table->integer('last_activity')->index();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private function tableRecords(): void
|
||||||
|
{
|
||||||
|
Database::make()->getSchemaBuilder()->create('records', function (Blueprint $table) {
|
||||||
|
$table->uuid('id')->primary();
|
||||||
|
$table->string('schema');
|
||||||
|
$table->string('status');
|
||||||
|
$table->jsonb('data');
|
||||||
|
$table->jsonb('_sys');
|
||||||
|
$table->jsonb('_file');
|
||||||
|
$table->text('search')->default("");
|
||||||
|
|
||||||
|
$table->index(['schema', '_sys->updatedAt', 'status']);
|
||||||
|
$table->index('search');
|
||||||
|
});
|
||||||
|
|
||||||
|
Database::make()->getSchemaBuilder()->create('edges', function (Blueprint $table) {
|
||||||
|
$table->uuid('source');
|
||||||
|
$table->uuid('target');
|
||||||
|
$table->string('sourceSchema');
|
||||||
|
$table->string('targetSchema');
|
||||||
|
$table->string('field');
|
||||||
|
$table->string('rank');
|
||||||
|
|
||||||
|
$table->unique(['source', 'target', "field"]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private function tableRevisions(): void
|
||||||
|
{
|
||||||
|
Database::make()->getSchemaBuilder()->create('revisions', function (Blueprint $table) {
|
||||||
|
$table->uuid('id')->primary();
|
||||||
|
$table->uuid('recordId');
|
||||||
|
$table->string('schema');
|
||||||
|
$table->jsonb('data');
|
||||||
|
$table->jsonb('_sys');
|
||||||
|
$table->jsonb('_file');
|
||||||
|
$table->jsonb('_edges');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private function tableCommandLogs(): void
|
||||||
|
{
|
||||||
|
Database::make()->getSchemaBuilder()->create('command_logs', function (Blueprint $table) {
|
||||||
|
$table->uuid('id')->primary();
|
||||||
|
$table->string('signature');
|
||||||
|
$table->integer('pid')->nullable();
|
||||||
|
$table->text('logs');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user