transition
This commit is contained in:
+3
-2
@@ -8,13 +8,14 @@
|
|||||||
"ext-zip": "*",
|
"ext-zip": "*",
|
||||||
"ext-sqlite3": "*",
|
"ext-sqlite3": "*",
|
||||||
"ext-imagick": "*",
|
"ext-imagick": "*",
|
||||||
"php": "^8.2",
|
"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": "*"
|
"ext-pdo": "*",
|
||||||
|
"mustache/mustache": "^2.14"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpstan/phpstan": "^1.8"
|
"phpstan/phpstan": "^1.8"
|
||||||
|
|||||||
Generated
+56
-2
@@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "351290446963296c5fabf750b3077a95",
|
"content-hash": "cd811774f135eb0a9c0338113aa84d8e",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "brick/math",
|
"name": "brick/math",
|
||||||
@@ -947,6 +947,56 @@
|
|||||||
],
|
],
|
||||||
"time": "2022-05-21T17:30:32+00:00"
|
"time": "2022-05-21T17:30:32+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "mustache/mustache",
|
||||||
|
"version": "v2.14.2",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/bobthecow/mustache.php.git",
|
||||||
|
"reference": "e62b7c3849d22ec55f3ec425507bf7968193a6cb"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/bobthecow/mustache.php/zipball/e62b7c3849d22ec55f3ec425507bf7968193a6cb",
|
||||||
|
"reference": "e62b7c3849d22ec55f3ec425507bf7968193a6cb",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.2.4"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"friendsofphp/php-cs-fixer": "~1.11",
|
||||||
|
"phpunit/phpunit": "~3.7|~4.0|~5.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-0": {
|
||||||
|
"Mustache": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Justin Hileman",
|
||||||
|
"email": "justin@justinhileman.info",
|
||||||
|
"homepage": "http://justinhileman.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A Mustache implementation in PHP.",
|
||||||
|
"homepage": "https://github.com/bobthecow/mustache.php",
|
||||||
|
"keywords": [
|
||||||
|
"mustache",
|
||||||
|
"templating"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/bobthecow/mustache.php/issues",
|
||||||
|
"source": "https://github.com/bobthecow/mustache.php/tree/v2.14.2"
|
||||||
|
},
|
||||||
|
"time": "2022-08-23T13:07:01+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "nesbot/carbon",
|
"name": "nesbot/carbon",
|
||||||
"version": "2.71.0",
|
"version": "2.71.0",
|
||||||
@@ -2261,7 +2311,11 @@
|
|||||||
"prefer-stable": true,
|
"prefer-stable": true,
|
||||||
"prefer-lowest": false,
|
"prefer-lowest": false,
|
||||||
"platform": {
|
"platform": {
|
||||||
"php": "^8.2",
|
"ext-xml": "*",
|
||||||
|
"ext-zip": "*",
|
||||||
|
"ext-sqlite3": "*",
|
||||||
|
"ext-imagick": "*",
|
||||||
|
"php": "^8.3",
|
||||||
"ext-pdo": "*"
|
"ext-pdo": "*"
|
||||||
},
|
},
|
||||||
"platform-dev": [],
|
"platform-dev": [],
|
||||||
|
|||||||
Vendored
+3
@@ -5,6 +5,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import {loadHtmxFormsBehaviour} from "./htmx-form.js";
|
||||||
|
|
||||||
|
loadHtmxFormsBehaviour();
|
||||||
window.axios = axios;
|
window.axios = axios;
|
||||||
export const axiosInstance = axios;
|
export const axiosInstance = axios;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
export function loadHtmxFormsBehaviour(){
|
||||||
|
document.querySelectorAll(".form").forEach(el => {
|
||||||
|
initHtmxForm(el);
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function initHtmxForm(el){
|
||||||
|
el.addEventListener("htmx:responseError", (e) => {
|
||||||
|
el.querySelector(".form-errors").innerHTML = e.detail.xhr.response;
|
||||||
|
});
|
||||||
|
|
||||||
|
const formEl = el.querySelector("form");
|
||||||
|
|
||||||
|
if(!formEl.getAttribute("hx-redirect")){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
el.addEventListener("htmx:afterOnLoad", (e) => {
|
||||||
|
if(e.detail.successful){
|
||||||
|
return window.location.href = formEl.getAttribute("hx-redirect");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import Account from "./svelte/Account.svelte";
|
|||||||
import Channel from "./svelte/Channel.svelte";
|
import Channel from "./svelte/Channel.svelte";
|
||||||
import * as bootstrap from "bootstrap";
|
import * as bootstrap from "bootstrap";
|
||||||
import Mustache from "mustache";
|
import Mustache from "mustache";
|
||||||
|
import 'htmx.org';
|
||||||
|
|
||||||
Mustache.escape = function (value) {
|
Mustache.escape = function (value) {
|
||||||
return value;
|
return value;
|
||||||
|
|||||||
Generated
+7
-1
@@ -5,7 +5,8 @@
|
|||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/lang-markdown": "^6.2.2"
|
"@codemirror/lang-markdown": "^6.2.2",
|
||||||
|
"htmx.org": "^2.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@codemirror/commands": "^6.1.2",
|
"@codemirror/commands": "^6.1.2",
|
||||||
@@ -935,6 +936,11 @@
|
|||||||
"node": ">= 0.4.0"
|
"node": ">= 0.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/htmx.org": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-wO/rWlveSLD2mzRS9Em0v5hlsi6r21iUvaS17GPMgehBbM7eoQmqGQCkscsM67poF24zONgq3gQv+q/cgCHn2w=="
|
||||||
|
},
|
||||||
"node_modules/immutable": {
|
"node_modules/immutable": {
|
||||||
"version": "4.3.4",
|
"version": "4.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz",
|
||||||
|
|||||||
+2
-1
@@ -29,6 +29,7 @@
|
|||||||
"vite": "^3.2.3"
|
"vite": "^3.2.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/lang-markdown": "^6.2.2"
|
"@codemirror/lang-markdown": "^6.2.2",
|
||||||
|
"htmx.org": "^2.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.8 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 195 KiB |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><circle cx="4" cy="12" r="3" fill="currentColor"><animate id="svgSpinners3DotsBounce0" attributeName="cy" begin="0;svgSpinners3DotsBounce1.end+0.25s" calcMode="spline" dur="0.6s" keySplines=".33,.66,.66,1;.33,0,.66,.33" values="12;6;12"/></circle><circle cx="12" cy="12" r="3" fill="currentColor"><animate attributeName="cy" begin="svgSpinners3DotsBounce0.begin+0.1s" calcMode="spline" dur="0.6s" keySplines=".33,.66,.66,1;.33,0,.66,.33" values="12;6;12"/></circle><circle cx="20" cy="12" r="3" fill="currentColor"><animate id="svgSpinners3DotsBounce1" attributeName="cy" begin="svgSpinners3DotsBounce0.begin+0.2s" calcMode="spline" dur="0.6s" keySplines=".33,.66,.66,1;.33,0,.66,.33" values="12;6;12"/></circle></svg>
|
||||||
|
After Width: | Height: | Size: 803 B |
@@ -0,0 +1,22 @@
|
|||||||
|
.scope-login {
|
||||||
|
display: flex;
|
||||||
|
height: 100vh;
|
||||||
|
|
||||||
|
.bg-image {
|
||||||
|
width: 50%;
|
||||||
|
background: url("/vendor/lucent/public/art.jpg");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form{
|
||||||
|
width: 50%;
|
||||||
|
height: 100vh;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input{
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
}
|
||||||
|
input,textarea{
|
||||||
|
background: var(--input-bg);
|
||||||
|
border: 1px solid $border-color;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 5px 7px;
|
||||||
|
font-size: 16px;
|
||||||
|
filter: brightness(95%);
|
||||||
|
}
|
||||||
|
input:focus,textarea:focus{
|
||||||
|
filter: brightness(101%);
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea{
|
||||||
|
resize: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.htmx-indicator {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.htmx-request .htmx-indicator {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.htmx-request.htmx-indicator {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.bt {
|
||||||
|
appearance: none;
|
||||||
|
background-color: #000;
|
||||||
|
background-image: none;
|
||||||
|
border: 1px solid #000;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: #fff 4px 4px 0 0, #000 4px 4px 0 1px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
font-family: ITCAvantGardeStd-Bk, Arial, sans-serif;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 20px;
|
||||||
|
margin: 0 5px 10px 0;
|
||||||
|
overflow: visible;
|
||||||
|
padding: 8px 40px;
|
||||||
|
text-align: center;
|
||||||
|
text-transform: none;
|
||||||
|
touch-action: manipulation;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
vertical-align: middle;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bt:focus {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bt:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bt:active {
|
||||||
|
box-shadow: rgba(0, 0, 0, .125) 0 3px 5px inset;
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bt:not([disabled]):active {
|
||||||
|
box-shadow: #fff 2px 2px 0 0, #000 2px 2px 0 1px;
|
||||||
|
transform: translate(2px, 2px);
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
.mt-1{margin-top: 4px}
|
||||||
|
.mt-2{margin-top: 8px}
|
||||||
|
.mt-3{margin-top: 12px}
|
||||||
|
.mt-4{margin-top: 16px}
|
||||||
|
.mt-5{margin-top: 20px}
|
||||||
|
|
||||||
|
.mb-1{margin-bottom: 4px}
|
||||||
|
.mb-2{margin-bottom: 8px}
|
||||||
|
.mb-3{margin-bottom: 12px}
|
||||||
|
.mb-4{margin-bottom: 16px}
|
||||||
|
.mb-5{margin-bottom: 20px}
|
||||||
|
|
||||||
|
.pt-1{padding-top: 4px}
|
||||||
|
.pt-2{padding-top: 8px}
|
||||||
|
.pt-3{padding-top: 12px}
|
||||||
|
.pt-4{padding-top: 16px}
|
||||||
|
.pt-5{padding-top: 20px}
|
||||||
|
|
||||||
|
.pb-1{padding-bottom: 4px}
|
||||||
|
.pb-2{padding-bottom: 8px}
|
||||||
|
.pb-3{padding-bottom: 12px}
|
||||||
|
.pb-4{padding-bottom: 16px}
|
||||||
|
.pb-5{padding-bottom: 20px}
|
||||||
|
|
||||||
|
.gap-1{gap: 4px}
|
||||||
|
.gap-2{gap: 8px}
|
||||||
|
.gap-3{gap: 12px}
|
||||||
|
.gap-4{gap: 16px}
|
||||||
|
.gap-5{gap: 20px}
|
||||||
|
|
||||||
|
.hide{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d-block{
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d-inline-block{
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-bold{
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.in-place{
|
||||||
|
padding: 36px;
|
||||||
|
}
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
.in-place{
|
|
||||||
padding: 36px;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
.sidebar {
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-wrapper {
|
||||||
|
display: flex;
|
||||||
|
gap: 40px;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
.lx-nav{
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
background-color: rgba(255,255,255,1);
|
|
||||||
margin-bottom:0px ;
|
|
||||||
.nav-item{
|
|
||||||
padding:16px 0;
|
|
||||||
margin: 0 16px;
|
|
||||||
color: $primary;
|
|
||||||
|
|
||||||
}
|
|
||||||
a{
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
.notice {
|
||||||
|
background-color: $background;
|
||||||
|
padding: 25px 14px 14px;
|
||||||
|
margin: 2rem 0;
|
||||||
|
filter: brightness(1.03);
|
||||||
|
position: relative;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 24px;
|
||||||
|
border: 3px solid $border-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice .title {
|
||||||
|
content: "NOTE";
|
||||||
|
position: absolute;
|
||||||
|
background: $background;
|
||||||
|
min-width: 90px;
|
||||||
|
border: 3px solid $border-color;
|
||||||
|
color: $text;
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
left: 14px;
|
||||||
|
top: -18px;
|
||||||
|
padding: 2px 10px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice.success{
|
||||||
|
border-color: $success;
|
||||||
|
& .title{
|
||||||
|
border-color: $success;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
1. Use a more-intuitive box-sizing model.
|
||||||
|
*/
|
||||||
|
*, *::before, *::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
2. Remove default margin
|
||||||
|
*/
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
Typographic tweaks!
|
||||||
|
3. Add accessible line-height
|
||||||
|
4. Improve text rendering
|
||||||
|
*/
|
||||||
|
body {
|
||||||
|
line-height: 1.5;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
5. Improve media defaults
|
||||||
|
*/
|
||||||
|
img, picture, video, canvas, svg {
|
||||||
|
display: block;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
6. Remove built-in form typography styles
|
||||||
|
*/
|
||||||
|
input, button, textarea, select {
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
7. Avoid text overflows
|
||||||
|
*/
|
||||||
|
p, h1, h2, h3, h4, h5, h6 {
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
8. Create a root stacking context
|
||||||
|
*/
|
||||||
|
#root, #__next {
|
||||||
|
isolation: isolate;
|
||||||
|
}
|
||||||
+37
-77
@@ -1,89 +1,49 @@
|
|||||||
|
|
||||||
.sidebar {
|
.sidebar {
|
||||||
background-color: $dark;
|
border: 1px solid $border-color;
|
||||||
min-height: 100vh;
|
border-radius: 12px;
|
||||||
font-size: 15px;
|
min-height: 100vh;
|
||||||
line-height: 28px;
|
font-size: 15px;
|
||||||
|
line-height: 28px;
|
||||||
|
min-width: 300px;
|
||||||
|
max-width: 400px;
|
||||||
|
background: $background;
|
||||||
|
filter: brightness(104%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-header {
|
.sidebar-header {
|
||||||
color: $white;
|
background: $background;
|
||||||
position: relative;
|
font-size: 16px;
|
||||||
padding: 0 10px;
|
padding: 12px 12px 6px;
|
||||||
white-space: nowrap;
|
color: $text;
|
||||||
text-overflow: ellipsis;
|
filter: brightness(94%);
|
||||||
overflow: hidden;
|
|
||||||
&:hover .actions {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar .actions {
|
&:first-child {
|
||||||
position: absolute;
|
border-bottom: none;
|
||||||
top: 0px;
|
border-radius: 12px 12px 0 0;
|
||||||
right: 8px;
|
}
|
||||||
background-color: $dark;
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
.sidebar .sidebar-header .actions {
|
|
||||||
background-color: $dark;
|
|
||||||
}
|
|
||||||
.sidebar .sidebar-item .actions {
|
|
||||||
background-color: $white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar .sidebar-item.active .actions {
|
|
||||||
background-color: $primary;
|
|
||||||
}
|
|
||||||
.sidebar .sidebar-header .actions a,
|
|
||||||
.sidebar .sidebar-header .actions span {
|
|
||||||
color: $white !important;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar .sidebar-item .actions a {
|
|
||||||
color: $dark !important;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar .sidebar-item.active .actions a {
|
|
||||||
color: $white !important;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-item {
|
.sidebar-item {
|
||||||
a {
|
|
||||||
color: $white;
|
|
||||||
text-decoration: none;
|
|
||||||
padding: 0 0px;
|
|
||||||
}
|
|
||||||
&:hover {
|
|
||||||
background-color: $light;
|
|
||||||
a {
|
|
||||||
color: $dark;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
position: relative;
|
|
||||||
padding: 0 10px;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
color: $white;
|
|
||||||
&:hover .actions {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
color: $text;
|
||||||
|
display: block;
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
text-decoration: none;
|
||||||
|
border-bottom: 1px solid $border-color;
|
||||||
|
transition: 600ms;
|
||||||
|
|
||||||
}
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
.sidebar hr {
|
border-radius: 0 0 12px 12px;
|
||||||
color: $white;
|
}
|
||||||
line-height: 30px;
|
|
||||||
}
|
&:hover {
|
||||||
|
background: $secondary;
|
||||||
.sidebar .active {
|
}
|
||||||
background-color: $primary;
|
|
||||||
a {
|
&.active {
|
||||||
color: $white;
|
background: $secondary;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
.lx-table {
|
.lx-table {
|
||||||
|
min-width: 600px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
background-color: #f9f9f9;
|
|
||||||
table {
|
table {
|
||||||
width: 100%;
|
min-width: 600px;
|
||||||
|
|
||||||
}
|
}
|
||||||
th {
|
th {
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
.lx-small-text {
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 15px;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
|
||||||
|
|
||||||
|
.content{
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 20px;
|
||||||
|
p{
|
||||||
|
|
||||||
|
margin-bottom: 14px;
|
||||||
|
&:last-child{
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
padding: 0 0 0 16px;
|
||||||
|
list-style: none outside none;
|
||||||
|
|
||||||
|
li::before {
|
||||||
|
content: "—";
|
||||||
|
opacity: .5;
|
||||||
|
font-size: 12px;
|
||||||
|
padding-right: 6px;
|
||||||
|
vertical-align: 10%;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-separator{
|
||||||
|
font-size: 20px;
|
||||||
|
text-align: center;
|
||||||
|
padding: 30px 0 10px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
position: relative;
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
//&:after {
|
||||||
|
// position: absolute;
|
||||||
|
// left: 0;
|
||||||
|
// right: 0;
|
||||||
|
// top: 70px;
|
||||||
|
// margin: auto;
|
||||||
|
// display: block;
|
||||||
|
// width: 270px;
|
||||||
|
// border-bottom: 1px solid var(--border-color);
|
||||||
|
// content: "";
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
.lx-small-text {
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 15px;
|
||||||
|
}
|
||||||
@@ -120,6 +120,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.header-small {
|
.header-small {
|
||||||
text-align: center;
|
text-align: left;
|
||||||
font-size: 26px;
|
font-size: 26px;
|
||||||
}
|
}
|
||||||
|
|||||||
+58
-8
@@ -6,6 +6,10 @@ $green-pigment: #139a43ff;
|
|||||||
$lincoln-green: #0b5d1eff;
|
$lincoln-green: #0b5d1eff;
|
||||||
$forest-green-traditional: #053b06ff;
|
$forest-green-traditional: #053b06ff;
|
||||||
$black: #000000ff;
|
$black: #000000ff;
|
||||||
|
$dark: #000000ff;
|
||||||
|
$white: #ffffff;
|
||||||
|
$light: #eee;
|
||||||
|
$danger: red;
|
||||||
|
|
||||||
/* SCSS Gradient */
|
/* SCSS Gradient */
|
||||||
$gradient-top: linear-gradient(
|
$gradient-top: linear-gradient(
|
||||||
@@ -80,25 +84,71 @@ $gradient-radial: radial-gradient(
|
|||||||
#000000ff
|
#000000ff
|
||||||
);
|
);
|
||||||
|
|
||||||
$primary: $lincoln-green;
|
$primary: #5b86be;
|
||||||
|
$secondary: #d9cca1;
|
||||||
|
$success: #80c671;
|
||||||
|
$background: #f4f6fa;
|
||||||
$table-striped-bg-factor: 0.03;
|
$table-striped-bg-factor: 0.03;
|
||||||
$dropdown-bg: rgb(206, 223, 210);
|
$dropdown-bg: rgb(206, 223, 210);
|
||||||
|
$border-color: #000;
|
||||||
|
$text: #04060b;
|
||||||
|
$accent: #80c671;
|
||||||
|
|
||||||
@import "../node_modules/bootstrap/scss/bootstrap";
|
//https://www.realtimecolors.com/?colors=04060b-f4f6fa-5b86be-d9cca1-80c671&fonts=Anek Telugu-Anek Telugu
|
||||||
|
$themes: (
|
||||||
|
light: (
|
||||||
|
text: #04060b,
|
||||||
|
background: #f4f6fa,
|
||||||
|
primary: #5b86be,
|
||||||
|
secondary: #d9cca1,
|
||||||
|
accent: #80c671,
|
||||||
|
),
|
||||||
|
dark: (
|
||||||
|
text: #f4f6fb,
|
||||||
|
background: #05070a,
|
||||||
|
primary: #416ca4,
|
||||||
|
secondary: #5e5126,
|
||||||
|
accent: #488e39,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//@import "../node_modules/bootstrap/scss/bootstrap";
|
||||||
|
@import "./reset";
|
||||||
|
@import "./helpers";
|
||||||
|
@import "./notice";
|
||||||
|
@import "./auth";
|
||||||
|
@import "./typography";
|
||||||
@import "./sidebar";
|
@import "./sidebar";
|
||||||
|
@import "./form";
|
||||||
@import "./table";
|
@import "./table";
|
||||||
@import "./avatar";
|
@import "./avatar";
|
||||||
@import "./codemirror";
|
@import "./codemirror";
|
||||||
|
@import "./layout";
|
||||||
@import "./wrappers";
|
@import "./wrappers";
|
||||||
@import "./in-place";
|
|
||||||
@import "./text";
|
|
||||||
@import "./card";
|
@import "./card";
|
||||||
@import "./nav";
|
|
||||||
@import "./files";
|
@import "./files";
|
||||||
@import "./revisions";
|
@import "./revisions";
|
||||||
|
|
||||||
|
:root{
|
||||||
|
--linearPrimarySecondary: linear-gradient(#5b86be, #d9cca1);
|
||||||
|
--linearPrimaryAccent: linear-gradient(#5b86be, #80c671);
|
||||||
|
--linearSecondaryAccent: linear-gradient(#d9cca1, #80c671);
|
||||||
|
--radialPrimarySecondary: radial-gradient(#5b86be, #d9cca1);
|
||||||
|
--radialPrimaryAccent: radial-gradient(#5b86be, #80c671);
|
||||||
|
--radialSecondaryAccent: radial-gradient(#d9cca1, #80c671);
|
||||||
|
--border-color: $border-color;
|
||||||
|
--main-font: ‘Open Sans‘, Arial, Helvetica, sans-serif;
|
||||||
|
--main-font-color: #444;
|
||||||
|
--input-bg: rgb(245,245,249);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background-color: rgba(11, 93, 30, 0.04);
|
background-color: $background;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-spinner .spinner-border {
|
.btn-spinner .spinner-border {
|
||||||
@@ -119,8 +169,8 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-menu {
|
.dropdown-menu {
|
||||||
border: 0px;
|
border: 0;
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
box-shadow: 0px 0px 4px #ccc;
|
box-shadow: 0 0 4px #ccc;
|
||||||
padding: 30px 15px;
|
padding: 30px 15px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
|
||||||
|
<x-lucent::notice type="success" title="Success">
|
||||||
|
<p>
|
||||||
|
If you have provided a valid email you should receive in the following seconds a login email
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>You can safely close this tab</p>
|
||||||
|
</x-lucent::notice>
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
@extends("lucent::layouts.account")
|
||||||
|
|
||||||
|
@section("content")
|
||||||
|
<div class="scope-login">
|
||||||
|
<div class="bg-image">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="login-form">
|
||||||
|
<div class="form">
|
||||||
|
<h2 class="mb-5">Enter Lucent</h2>
|
||||||
|
|
||||||
|
<form hx-post="/lucent/login" >
|
||||||
|
<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>
|
||||||
|
<div class="mt-5 mb-3">
|
||||||
|
<label for="emailaddress" class="form-label">Email address</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
name="email"
|
||||||
|
class="form-control"
|
||||||
|
id="emailaddress"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<x-lucent::button-indicator>
|
||||||
|
Send email
|
||||||
|
</x-lucent::button-indicator>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
@extends("lucent::layouts.account")
|
||||||
|
|
||||||
|
@section("content")
|
||||||
|
<div class="scope-login">
|
||||||
|
<div class="bg-image">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="login-form">
|
||||||
|
<div class="form">
|
||||||
|
<h2 class="mb-5">Welcome to Lucent</h2>
|
||||||
|
|
||||||
|
<form hx-post="/lucent/verify" hx-redirect="/lucent" hx-target-error=".form-errors" >
|
||||||
|
<input type="hidden" value="{{$email}}" name="email" />
|
||||||
|
<input type="hidden" value="{{$token}}" name="token" />
|
||||||
|
@csrf
|
||||||
|
<x-lucent::button-indicator>
|
||||||
|
Enter as {{$email}}
|
||||||
|
</x-lucent::button-indicator>
|
||||||
|
</form>
|
||||||
|
<div class="form-errors"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
@php
|
||||||
|
$side = $side ?? 48;
|
||||||
|
|
||||||
|
$colors = [
|
||||||
|
"#00AA55",
|
||||||
|
"#009FD4",
|
||||||
|
"#B381B3",
|
||||||
|
"#939393",
|
||||||
|
"#E3BC00",
|
||||||
|
"#D47500",
|
||||||
|
"#DC2A2A",
|
||||||
|
"#3ede91",
|
||||||
|
"#377dd4",
|
||||||
|
"#0256b0",
|
||||||
|
"#053d82",
|
||||||
|
"#3d026e",
|
||||||
|
"#b378e3",
|
||||||
|
"#c4065c",
|
||||||
|
"#543208",
|
||||||
|
"#d97811",
|
||||||
|
"#0c6b40",
|
||||||
|
];
|
||||||
|
$initials = function($name){
|
||||||
|
$segs = explode(" ",$name);
|
||||||
|
if(count($segs) > 1){
|
||||||
|
return strtoupper($segs[0][0]).strtoupper($segs[1][0]);
|
||||||
|
}
|
||||||
|
return strtoupper($segs[0][0]).strtoupper($segs[0][1]);
|
||||||
|
};
|
||||||
|
|
||||||
|
$name = $user["name"];
|
||||||
|
$charIndex = ord($name[1]) + strlen($name);
|
||||||
|
$colorIndex = $charIndex % 19;
|
||||||
|
$bgColor = $colors[$colorIndex];
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="avatar"
|
||||||
|
title="{{$name}}"
|
||||||
|
style="background-color:{{$bgColor}};height: {{$side}}px;width: {{$side}}px; font-size:{{$side / 2}}px"
|
||||||
|
>
|
||||||
|
<div class="avatar__letters">{{$initials($user["name"])}}</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<button class="bt bt-primary">
|
||||||
|
{{$slot}}
|
||||||
|
<img alt="indicator" id="indicator" class="htmx-indicator" src="/img/spinner.svg"/>
|
||||||
|
</button>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<div class="notice {{$type ?? "info"}}">
|
||||||
|
<div class="title">{{$title}}</div>
|
||||||
|
<div class="content">{{ $slot }}</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
@if (count($errors) > 0)
|
||||||
|
<x-lucent::notice type="error" title="🛑 Submission failed">
|
||||||
|
<ul>
|
||||||
|
@foreach ($errors as $error)
|
||||||
|
<li>{{ $error }}</li>
|
||||||
|
@endforeach
|
||||||
|
</ul>
|
||||||
|
</x-lucent::notice>
|
||||||
|
@endif
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
@extends("lucent::layouts.channel")
|
||||||
|
|
||||||
|
@section("content")
|
||||||
|
<h3 class="header-small mb-4">Latest Content changes</h3>
|
||||||
|
|
||||||
|
|
||||||
|
@if($records->isNotEmpty())
|
||||||
|
<div class="lx-card mb-4">
|
||||||
|
<div class="lx-table p-0">
|
||||||
|
<table class="">
|
||||||
|
<tbody>
|
||||||
|
@foreach($records as $record)
|
||||||
|
<tr>
|
||||||
|
@include("lucent::records.card-row")
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@endsection
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<div class="d-flex align-items-center ">
|
||||||
|
<a class="nav-item" href="/lucent">{{$channel->name}}</a>
|
||||||
|
<a class="nav-item" href="{channel.lucentUrl}/members">Members</a>
|
||||||
|
|
||||||
|
@if($channel->generateCommand)
|
||||||
|
<a href="{channel.lucentUrl}/build-report" class="btn btn-outline-primary btn-sm d-">Build website</a>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<!-- <div>-->
|
||||||
|
<!-- <form method="GET">-->
|
||||||
|
<!-- <input type="search" name="filter[search_regex]" placeholder="Search"-->
|
||||||
|
<!-- class="form-control" required/>-->
|
||||||
|
<!-- </form>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<a class="nav-item" href="/lucent/profile">
|
||||||
|
<x-lucent::avatar side="28" :user="$user"></x-lucent::avatar>
|
||||||
|
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
<html lang="en">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||||
<title>@yield('title') - Lucent Data Platform</title>
|
<title>@yield('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.css']["file"] }}"/>
|
<link rel="stylesheet" href="/vendor/lucent/dist/{{ $manifest['main.css']["file"] }}"/>
|
||||||
<script type="module" src="/vendor/lucent/dist/{{ $manifest['main.js']["file"] }}"></script>
|
<script type="module" src="/vendor/lucent/dist/{{ $manifest['main.js']["file"] }}"></script>
|
||||||
@@ -20,15 +20,14 @@
|
|||||||
@endif
|
@endif
|
||||||
|
|
||||||
|
|
||||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
{{-- <link rel="icon" type="image/x-icon" href="/favicon.ico"/>--}}
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="view-{{ $view }}">
|
<body>
|
||||||
<div class="mt-5">
|
<div>
|
||||||
@yield('content')
|
@yield('content')
|
||||||
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
@@ -22,9 +22,14 @@
|
|||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="view-{{ $view }}">
|
<body>
|
||||||
@yield('content')
|
@include("lucent::includes.header")
|
||||||
|
<div class="main-wrapper">
|
||||||
|
@include("lucent::sidebar.sidebar")
|
||||||
|
<div class="main-content">
|
||||||
|
@yield('content')
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
@php
|
||||||
|
$schema = $schemas->where("name",$record->schema)->first();
|
||||||
|
@endphp
|
||||||
|
<td>
|
||||||
|
{{-- {#if schema.type === "files"}--}}
|
||||||
|
{{-- <Preview {record} size="tiny"/>--}}
|
||||||
|
{{-- {:else}--}}
|
||||||
|
<a
|
||||||
|
href="/lucent/records/{{$record->id}}"
|
||||||
|
class="text-decoration-none text-dark d-block"
|
||||||
|
>
|
||||||
|
{{$viewModel->getRecordName($record, $schemas)}}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
{{$record->status->value === "draft" ? "Draft" : ""}}
|
||||||
|
{{-- {/if}--}}
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td><a
|
||||||
|
class="text-decoration-none lx-small-text"
|
||||||
|
href="/lucent/content/{{$schema->name}}">{{$schema->label}}</a
|
||||||
|
>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
|
||||||
|
<td>
|
||||||
|
{{-- <div class="d-flex">--}}
|
||||||
|
{{-- <Avatar name={usernameById(users, record._sys.updatedBy)} side={24}/>--}}
|
||||||
|
{{-- <div class="ms-2">--}}
|
||||||
|
{{-- {frieldlyUpdatedAt}--}}
|
||||||
|
{{-- </div>--}}
|
||||||
|
{{-- </div>--}}
|
||||||
|
</td>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
@php
|
||||||
|
$currentSchema = $currentSchema ?? null;
|
||||||
|
$activeClass = $schema->name === $currentSchema?->name ? "active" : "";
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
<a class="sidebar-item {{$activeClass}}" aria-current="page"
|
||||||
|
href="/lucent/content/{{$schema->name}}">{{$schema->label}}</a>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<div class="sidebar">
|
||||||
|
<div class="sidebar-header">
|
||||||
|
Content
|
||||||
|
</div>
|
||||||
|
@foreach($schemas->where("type.value", "collection")->where("isEntry",true) as $schema)
|
||||||
|
@include("lucent::sidebar.sidebar-item", ["schema" => $schema])
|
||||||
|
@endforeach
|
||||||
|
<div class="sidebar-header">
|
||||||
|
Files
|
||||||
|
</div>
|
||||||
|
@foreach($schemas->where("type.value", "files") as $schema)
|
||||||
|
@include("lucent::sidebar.sidebar-item", ["schema" => $schema])
|
||||||
|
@endforeach
|
||||||
|
<div class="sidebar-header">
|
||||||
|
Other
|
||||||
|
</div>
|
||||||
|
@foreach($schemas->where("type.value", "collection")->where("isEntry",false) as $schema)
|
||||||
|
@include("lucent::sidebar.sidebar-item", ["schema" => $schema])
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
@@ -119,24 +119,20 @@ readonly class AuthService
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws LucentException
|
public function sendLoginEmail(string $email): void
|
||||||
*/
|
|
||||||
public
|
|
||||||
function sendLoginEmail(string $email): void
|
|
||||||
{
|
{
|
||||||
$emailAddress = (new Email($email));
|
$emailAddress = (new Email($email));
|
||||||
$user = $this->userRepo->findByEmail($emailAddress);
|
$user = $this->userRepo->findByEmail($emailAddress);
|
||||||
|
|
||||||
if ($user->isEmpty()) {
|
if ($user->isEmpty()) {
|
||||||
throw new LucentException("User not found");
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($user->get()->isRemoved()) {
|
if ($user->get()->isRemoved()) {
|
||||||
throw new LucentException("Cannot reset email if the user is not active");
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$newToken = $this->userRepo->updateLoginToken($user->get()->id);
|
$newToken = $this->userRepo->updateLoginToken($user->get()->id);
|
||||||
|
|
||||||
Mail::to($email)->send(
|
Mail::to($email)->send(
|
||||||
|
|||||||
@@ -13,11 +13,13 @@ use Lucent\Account\AuthService;
|
|||||||
use Lucent\Channel\ChannelService;
|
use Lucent\Channel\ChannelService;
|
||||||
use Lucent\LucentException;
|
use Lucent\LucentException;
|
||||||
use Lucent\Svelte\Svelte;
|
use Lucent\Svelte\Svelte;
|
||||||
|
use Lucent\Util\Form\FormException;
|
||||||
|
use Lucent\Util\Form\ResponseFormError;
|
||||||
use function Lucent\Response\fail;
|
use function Lucent\Response\fail;
|
||||||
use function Lucent\Response\ok;
|
use function Lucent\Response\ok;
|
||||||
|
|
||||||
|
|
||||||
class AuthController extends Controller
|
class AuthController
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly AuthService $authService,
|
private readonly AuthService $authService,
|
||||||
@@ -65,52 +67,39 @@ class AuthController extends Controller
|
|||||||
return ok();
|
return ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function login(): View|RedirectResponse
|
public function login()
|
||||||
{
|
{
|
||||||
|
|
||||||
if ($this->accountService->countUsers() == 0) {
|
if ($this->accountService->countUsers() == 0) {
|
||||||
return redirect($this->channelService->channel->lucentUrl . "/register");
|
return redirect($this->channelService->channel->lucentUrl . "/register");
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->svelte->render(
|
return view("lucent::auth.login");
|
||||||
layout: "account",
|
|
||||||
view: "login",
|
|
||||||
title: "Log in"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function postLogin(Request $request): Response
|
public function postLogin(Request $request)
|
||||||
{
|
{
|
||||||
try {
|
$this->authService->sendLoginEmail($request->input("email"));
|
||||||
$this->authService->sendLoginEmail($request->input("email"));
|
|
||||||
} catch (LucentException $th) {
|
|
||||||
return fail($th);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ok();
|
return view("lucent::auth.login-success");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function verify(Request $request): View
|
public function verify(Request $request): View
|
||||||
{
|
{
|
||||||
return $this->svelte->render(
|
return view("lucent::auth.verify", [
|
||||||
layout: "account",
|
"email" => $request->input("email"),
|
||||||
view: "verify",
|
"token" => $request->input("token"),
|
||||||
title: "Verify and enter",
|
]);
|
||||||
data: [
|
|
||||||
"email" => $request->input("email"),
|
|
||||||
"token" => $request->input("token"),
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function postVerify(Request $request): Response
|
public function postVerify(Request $request)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->authService->login($request->input("email"), $request->input("token"));
|
$this->authService->login($request->input("email"), $request->input("token"));
|
||||||
} catch (LucentException $th) {
|
} catch (LucentException $th) {
|
||||||
return fail($th);
|
return ResponseFormError::fromException($th);
|
||||||
}
|
}
|
||||||
return ok();
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -21,14 +21,36 @@ class HomeController extends Controller
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public function home(): View
|
public function home(Request $request): View
|
||||||
{
|
{
|
||||||
|
$urlParams = $request->all();
|
||||||
|
$users = $this->accountService->all();
|
||||||
|
|
||||||
|
$sort = data_get($urlParams, "sort") ?? "-_sys.updatedAt";
|
||||||
|
$filter = data_get($urlParams, "filter") ?? [];
|
||||||
|
$arguments = array_merge([
|
||||||
|
"schema_in" => $this->accountService->currentReadableSchemas(),
|
||||||
|
"status_in" => ["draft", "published"]
|
||||||
|
], $filter);
|
||||||
|
|
||||||
|
$limit = 10;
|
||||||
|
|
||||||
|
$graph = $this->query
|
||||||
|
->filter($arguments)
|
||||||
|
->limit($limit)
|
||||||
|
->childrenDepth(1)
|
||||||
|
->parentsDepth(0)
|
||||||
|
->sort($sort)
|
||||||
|
->run();
|
||||||
|
|
||||||
|
|
||||||
|
return view("lucent::home", [
|
||||||
|
"users" => $users,
|
||||||
|
"records" => $graph->getRootRecords(),
|
||||||
|
"graph" => toArray($graph),
|
||||||
|
"modalUrl" => $request->fullUrl(),
|
||||||
|
]);
|
||||||
|
|
||||||
return $this->svelte->render(
|
|
||||||
layout: "channel",
|
|
||||||
view: "homeIndex",
|
|
||||||
title: "Records",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function records(Request $request): Response
|
public function records(Request $request): Response
|
||||||
|
|||||||
@@ -4,15 +4,19 @@ namespace Lucent\Http\Middleware;
|
|||||||
|
|
||||||
use Closure;
|
use Closure;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\View;
|
||||||
use Lucent\Account\AccountService;
|
use Lucent\Account\AccountService;
|
||||||
use Lucent\Account\AuthService;
|
use Lucent\Account\AuthService;
|
||||||
use Lucent\Channel\ChannelService;
|
use Lucent\Channel\ChannelService;
|
||||||
|
use Lucent\ViewModel\ViewModel;
|
||||||
|
|
||||||
readonly class AuthMiddleware
|
readonly class AuthMiddleware
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private AuthService $authService,
|
private AuthService $authService,
|
||||||
private ChannelService $channelService
|
private AccountService $accountService,
|
||||||
|
private ChannelService $channelService,
|
||||||
|
private ViewModel $viewModel
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -22,9 +26,11 @@ readonly class AuthMiddleware
|
|||||||
if (!$this->authService->isLoggedIn()) {
|
if (!$this->authService->isLoggedIn()) {
|
||||||
return redirect($this->channelService->channel->lucentUrl . "/login");
|
return redirect($this->channelService->channel->lucentUrl . "/login");
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->authService->refreshSession();
|
$this->authService->refreshSession();
|
||||||
|
View::share("channel",$this->channelService->channel);
|
||||||
|
View::share("user",session("user"));
|
||||||
|
View::share("schemas",$this->channelService->channel->schemas->whereIn("name",$this->accountService->currentReadableSchemas())->values());
|
||||||
|
View::share("viewModel",$this->viewModel);
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace Lucent;
|
namespace Lucent;
|
||||||
|
|
||||||
use Illuminate\Routing\Router;
|
use Illuminate\Routing\Router;
|
||||||
|
use Illuminate\Support\Facades\Blade;
|
||||||
use Illuminate\Support\Facades\View;
|
use Illuminate\Support\Facades\View;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
use Intervention\Image\ImageManager;
|
use Intervention\Image\ImageManager;
|
||||||
@@ -58,7 +59,7 @@ class LucentServiceProvider extends ServiceProvider
|
|||||||
$router->aliasMiddleware('lucent.auth', \Lucent\Http\Middleware\AuthMiddleware::class);
|
$router->aliasMiddleware('lucent.auth', \Lucent\Http\Middleware\AuthMiddleware::class);
|
||||||
$router->aliasMiddleware('lucent.guest', \Lucent\Http\Middleware\GuestMiddleware::class);
|
$router->aliasMiddleware('lucent.guest', \Lucent\Http\Middleware\GuestMiddleware::class);
|
||||||
|
|
||||||
$this->loadViewsFrom(__DIR__ . '/Views', 'lucent');
|
$this->loadViewsFrom(__DIR__ . '/../front/views', 'lucent');
|
||||||
$this->loadRoutesFrom(__DIR__ . '/Http/web.php');
|
$this->loadRoutesFrom(__DIR__ . '/Http/web.php');
|
||||||
$this->loadRoutesFrom(__DIR__ . '/Http/api.php');
|
$this->loadRoutesFrom(__DIR__ . '/Http/api.php');
|
||||||
|
|
||||||
@@ -76,6 +77,7 @@ class LucentServiceProvider extends ServiceProvider
|
|||||||
View::share('manifest', $manifest);
|
View::share('manifest', $manifest);
|
||||||
View::share('image', app()->make(ImageService::class));
|
View::share('image', app()->make(ImageService::class));
|
||||||
View::share('file', app()->make(FileService::class));
|
View::share('file', app()->make(FileService::class));
|
||||||
|
Blade::anonymousComponentPath(__DIR__.'../front/views/components',"lucent");
|
||||||
|
|
||||||
$this->publishes([
|
$this->publishes([
|
||||||
__DIR__ . '/Config/main.php' => config_path('lucent.php'),
|
__DIR__ . '/Config/main.php' => config_path('lucent.php'),
|
||||||
@@ -83,6 +85,7 @@ class LucentServiceProvider extends ServiceProvider
|
|||||||
|
|
||||||
$this->publishes([
|
$this->publishes([
|
||||||
__DIR__ . '/../front/dist' => public_path('vendor/lucent/dist'),
|
__DIR__ . '/../front/dist' => public_path('vendor/lucent/dist'),
|
||||||
|
__DIR__ . '/../front/public' => public_path('vendor/lucent/public'),
|
||||||
], 'lucent');
|
], 'lucent');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lucent\Util\Form;
|
||||||
|
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
|
||||||
|
final class FormException extends Exception
|
||||||
|
{
|
||||||
|
// Redefine the exception so message isn't optional
|
||||||
|
public function __construct(string $message, int $code = 0, Exception $previous = null)
|
||||||
|
{
|
||||||
|
// make sure everything is assigned properly
|
||||||
|
parent::__construct($message, $code, $previous);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lucent\Util\Form;
|
||||||
|
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Illuminate\Contracts\Foundation\Application;
|
||||||
|
use Illuminate\Contracts\Routing\ResponseFactory;
|
||||||
|
use Illuminate\Http\Response;
|
||||||
|
use Illuminate\Validation\Validator;
|
||||||
|
use Lucent\LucentException;
|
||||||
|
|
||||||
|
class ResponseFormError
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param $errors list<string>
|
||||||
|
*/
|
||||||
|
public static function render(array $errors): ResponseFactory|Application|Response
|
||||||
|
{
|
||||||
|
return response(view("lucent::forms.errors", ["errors" => $errors])->render(), 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Validator $validator
|
||||||
|
* @return ResponseFactory|Application|Response
|
||||||
|
*/
|
||||||
|
public static function fromValidator(Validator $validator): ResponseFactory|Application|Response
|
||||||
|
{
|
||||||
|
|
||||||
|
return self::render($validator->errors()->all());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $errors
|
||||||
|
* @return ResponseFactory|Application|Response
|
||||||
|
*/
|
||||||
|
public static function fromArray(array $errors): ResponseFactory|Application|Response
|
||||||
|
{
|
||||||
|
return self::render($errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $errorString
|
||||||
|
* @return ResponseFactory|Application|Response
|
||||||
|
*/
|
||||||
|
public static function fromMessage(string $errorString): ResponseFactory|Application|Response
|
||||||
|
{
|
||||||
|
return self::render([$errorString]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param LucentException|FormException $exception
|
||||||
|
* @return ResponseFactory|Application|Response
|
||||||
|
*/
|
||||||
|
public static function fromException(LucentException|FormException $exception): ResponseFactory|Application|Response
|
||||||
|
{
|
||||||
|
return self::render([$exception->getMessage()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lucent\ViewModel;
|
||||||
|
|
||||||
|
use Lucent\Channel\ChannelService;
|
||||||
|
use Lucent\Record\QueryRecord;
|
||||||
|
use Lucent\Record\Status;
|
||||||
|
use Lucent\Schema\CollectionSchema;
|
||||||
|
use Lucent\Schema\FieldInterface;
|
||||||
|
use Lucent\Schema\FilesSchema;
|
||||||
|
use Mustache_Engine;
|
||||||
|
|
||||||
|
class ViewModel
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public ChannelService $channelService
|
||||||
|
)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getRecordName(QueryRecord $record): string
|
||||||
|
{
|
||||||
|
$schema = $this->channelService->getSchema($record->schema)->get();
|
||||||
|
if (empty($schema->titleTemplate)) {
|
||||||
|
$title = match (get_class($schema)) {
|
||||||
|
CollectionSchema::class => $record->data[$schema->fields->filter(fn(FieldInterface $f) => $f->info->name === "text")->first()->name],
|
||||||
|
FilesSchema::class => $record->_file->path,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (empty(trim($title))) {
|
||||||
|
return "~Untitled~";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $title;
|
||||||
|
}
|
||||||
|
|
||||||
|
$m = new Mustache_Engine(array('entity_flags' => ENT_QUOTES));
|
||||||
|
return $m->render($schema->titleTemplate, $record->data);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user