content and edit record

This commit is contained in:
2024-08-15 18:52:53 +03:00
parent f9806f60c9
commit 113533408d
38 changed files with 577 additions and 289 deletions
+14
View File
@@ -0,0 +1,14 @@
<script>
let checkboxEl = null;
export let indeterminate = false;
export let value;
export let checked = false;
</script>
<div class="checkbox-wrapper">
<input bind:this={checkboxEl} on:change id="c1-13" type="checkbox" {value} {indeterminate} checked={checked}/>
</div>
+3 -2
View File
@@ -3,7 +3,8 @@
</script>
{#if message}
<div class="alert alert-danger" role="alert">
{message}
<div class="notice notice-error" role="alert">
<div class="title">Submission Errors</div>
<div class="content"> {message}</div>
</div>
{/if}
+1 -1
View File
@@ -3,7 +3,7 @@
export let disabled = false;
</script>
<button type="submit" class="btn btn-primary btn-spinner" {disabled}>
<button type="submit" class="button btn-primary btn-spinner" {disabled}>
<span
class="spinner-border spinner-border-sm"
role="status"
+9
View File
@@ -0,0 +1,9 @@
<script>
export let value;
export let checked = false;
</script>
<input type="checkbox" {value} on:change
class="switch" {checked}/>
@@ -35,47 +35,47 @@
}
</script>
<div class="d-flex align-items-center mb-3">
<div style="display: flex;align-items: center; gap: 8px">
<span class="me-2">{selected.length} records selected</span>
<div class="btn-group " role="group" aria-label="Basic example">
<button
on:click|preventDefault={(e) => changeStatus(e, "published")}
type="button"
class="btn btn-sm btn-outline-primary">Publish
class="button">Publish
</button
>
<button
on:click|preventDefault={(e) => changeStatus(e, "draft")}
type="button"
class="btn btn-sm btn-outline-primary">Make Draft
class="button">Make Draft
</button
>
{#if filter["status_in"] === "trashed"}
<button
on:click|preventDefault={(e) => changeStatus(e, "published")}
type="button"
class="btn btn-sm btn-outline-primary">Publish
class="button">Publish
</button
>
{#if schema.hasDrafts}
<button
on:click|preventDefault={(e) => changeStatus(e, "draft")}
type="button"
class="btn btn-sm btn-outline-primary">Make Draft
class="button">Make Draft
</button
>
{/if}
<button
on:click|preventDefault={deleteRecords}
type="button"
class="btn btn-sm btn-outline-primary">Delete forever
class="button">Delete forever
</button
>
{:else}
<button
type="button"
on:click|preventDefault={(e) => changeStatus(e, "trashed")}
class="btn btn-sm btn-outline-primary">Move to trash
class="button">Move to trash
</button
>
{/if}
+39 -51
View File
@@ -5,6 +5,7 @@
import {getContext} from "svelte";
import Avatar from "../account/Avatar.svelte";
import {selectRecord, toggleAll} from "./functions/recordSelect.js";
import Checkbox from "../common/Checkbox.svelte";
const channel = getContext("channel");
@@ -20,7 +21,7 @@
export let selected = [];
function eventToggleAll(e) {
selected = toggleAll(e,records,selected)
selected = toggleAll(e, records, selected)
}
function select(record) {
@@ -31,20 +32,18 @@
</script>
<div class="table">
<table class="">
<thead class="table-light">
<div class="table mt-5 ">
<table>
<thead>
<tr>
{#if isWritable}
<th>
<input
on:change|preventDefault={eventToggleAll}
indeterminate={selected.length > 0 &&
selected.length < records.length}
checked={selected.length == records.length}
class="form-check-input"
type="checkbox"
/>
<Checkbox
on:change={eventToggleAll}
indeterminate={selected.length > 0 && selected.length < records.length}
checked={selected.length === records.length}
>
</Checkbox>
</th>
{/if}
@@ -54,13 +53,13 @@
class:is-sort={field.name === sortField.name}
scope="col"
title={field.help}
data-bs-toggle="tooltip"
data-bs-placement="top">{field.label}</th
>{field.label}</th
>
{/each}
{#each systemFields.filter(c => schema.visible.includes(c.name)) as sysField}
<th>{sysField.label}</th>
<th class:is-sort={sysField.name === sortField.name}>{sysField.label}</th>
{/each}
<th></th>
</tr>
</thead>
<tbody>
@@ -68,44 +67,26 @@
<tr>
<td class="title-td">
<div
class="title-td-contents d-inline-flex justify-content-between w-100 align-items-center"
class="title-td-contents"
>
<div class="d-flex align-items-center ">
{#if isWritable}
<div class="form-check">
<input
on:change={() => select(record)}
class="form-check-input "
type="checkbox"
checked={selected.find(
(r) => r.id === record.id
)}
value={record}
/>
</div>
{/if}
<a
class="me-2 text-decoration-none text-dark fs-6"
href="{channel.lucentUrl}/records/{record.id}"
target={inModal ? "_blank" : "_self"}
title={previewTitle(channel.schemas, record, graph)}
data-bs-toggle="tooltip" data-bs-placement="left"
{#if isWritable}
<Checkbox
on:change={() => select(record)}
checked={selected.find((r) => r.id === record.id)}
value={record}
>
{previewTitle(channel.schemas, record, graph)}
</a>
</div>
<div>
<Avatar
name={usernameById(
users,
record._sys.updatedBy
)}
side={24}
/>
</div>
</Checkbox>
{/if}
<a
class="me-2 text-decoration-none text-dark fs-6"
href="{channel.lucentUrl}/records/{record.id}"
target={inModal ? "_blank" : "_self"}
>
{previewTitle(channel.schemas, record, graph)}
</a>
</div>
</td>
<RecordRow
@@ -117,6 +98,13 @@
{sortField}
{users}
/>
<td> <Avatar
name={usernameById(
users,
record._sys.updatedBy
)}
side={24}
/></td>
</tr>
{/each}
</tbody>
@@ -28,8 +28,8 @@
</script>
{#each pages as i}
<li class="page-item">
{#if currentPage == i}
<li class="page-item" class:active={currentPage === i}>
{#if currentPage === i}
<span class="page-link active">{i}</span>
{:else}
<a class="page-link" on:click={(e) => goto(e, i)} href={url(i)}
@@ -43,7 +43,7 @@
</script>
<nav>
<ul class="pagination justify-content-center">
<ul class="pagination">
{#if totalPages > 1}
<li class="page-item disabled" class:disabled={currentPage === 1}>
<a on:click={first} href="/" class="page-link"> First </a>
@@ -69,7 +69,7 @@
{/if}
</ul>
</nav>
<p class="text-muted text-center">
<p style="display: flex;justify-content: center; gap: 4px">
Showing
<span class="font-medium">{+skip + 1}</span>
to
+2 -2
View File
@@ -74,13 +74,13 @@
<form method="GET" on:submit={search}>
<input type="search" name="filter[search_regex]" placeholder="Search"
class="form-control" required>
class="search" required>
</form>
</div>
<div style="display:flex;align-items: center">
<div style="display:flex;align-items: center;gap:4px">
{#if schema.type === "collection"}
{#if !inModal && isWritable}
<a
+3 -3
View File
@@ -7,6 +7,7 @@
import "tinymce/themes/silver";
import "tinymce/skins/ui/oxide/skin.css";
import contentUiSkinCss from "tinymce/skins/ui/oxide/content.css";
import customcss from "./tinymce.css";
import "tinymce/plugins/link";
import "tinymce/plugins/code";
@@ -52,7 +53,7 @@
toolbar_sticky: true,
skin: false,
content_css: false,
content_style: contentUiSkinCss.toString(),
content_style: contentUiSkinCss.toString() + customcss.toString(),
branding: false,
inline: false,
plugins: plugins,
@@ -67,8 +68,7 @@
browser_spellcheck: true,
max_height: 600,
// media_poster: false,
content_style:
"img {max-width: 100%;height: auto;",
setup: function (editor) {
activeEditor = editor;
+37
View File
@@ -0,0 +1,37 @@
.mce-content-body .img {
max-width: 100%;
height: auto;
}
.mce-content-body{
font-size: 16px;
line-height: 20px;
}
.mce-content-body p{
margin-bottom: 14px;
&:last-child{
margin-bottom: 0;
}
}
.mce-content-body ul {
padding: 0 0 0 16px;
list-style: none outside none;
}
.mce-content-body li::before {
content: "—";
opacity: .5;
font-size: 12px;
padding-right: 6px;
vertical-align: 10%;
}
.mce-content-body li {
list-style: none;
padding: 0;
}
+25 -33
View File
@@ -1,15 +1,15 @@
<script>
import {afterUpdate, getContext, onMount} from "svelte";
import {isEqual} from "lodash";
import Manager from "./Manager.svelte";
import EditHeader from "./EditHeader.svelte"
import StatusSelect from "./StatusSelect.svelte"
import axios from "axios";
import EditHeader from "./header/EditHeader.svelte"
import FilePreview from "./FilePreview.svelte"
import ContentTabs from "./ContentTabs.svelte"
import ContentTabs from "./header/ContentTabs.svelte"
import FormField from "./FormField.svelte"
import Graph from "./Graph.svelte"
import Info from "./Info.svelte"
import ErrorAlert from "../common/ErrorAlert.svelte"
import Title from "./header/Title.svelte";
const channel = getContext("channel");
@@ -151,47 +151,39 @@
<svelte:window on:beforeunload={beforeUnload}/>
<div class="wrapper-normal transparent">
<Manager managerRecords={recordHistory} {graph}/>
<EditHeader {schema} {record} {isCreateMode} {graph} bind:activeContentTab/>
{#if !["_graph", "_info"].includes(activeContentTab) && isWritable}
<div class="shadow-lg "
style="position:fixed;bottom:0;left:0px;width:100%;background: rgb(206, 223, 210);z-index:1050"
<div class="record-edit">
<div class="tools-header">
<!-- <Manager managerRecords={recordHistory} {graph}/>-->
{#if isCreateMode}
<button
class="button btn-spinner"
on:click={save}
>
<div
class="d-flex mt-3 mb-3 align-items-center justify-content-center"
>
<StatusSelect bind:status={record.status} {record} {schema}/>
{#if isCreateMode}
<button
class="ms-2 btn btn-primary btn-spinner"
on:click={save}
>
<span
class="spinner-border spinner-border-sm"
role="status"
aria-hidden="true"
/>
Create
</button>
{:else if hasUnsavedData}
<button
type="button"
class="ms-2 btn btn-primary btn-spinner"
on:click={save}
>
Create
</button>
{:else if hasUnsavedData}
<button
type="button"
class="button ms-2 btn btn-primary btn-spinner"
on:click={save}
>
<span
class="spinner-border spinner-border-sm"
role="status"
aria-hidden="true"
/>
Save
</button>
{/if}
</div>
</div>
Save
</button>
{/if}
<EditHeader {schema} bind:record {isCreateMode} {graph} bind:activeContentTab/>
</div>
<Title {schema} {record} {isCreateMode}/>
<ErrorAlert message={errorMessage}/>
-73
View File
@@ -1,73 +0,0 @@
<script>
import {getContext} from "svelte";
import Icon from "../common/Icon.svelte";
import {previewTitle} from "./Preview";
const channel = getContext("channel");
export let schema;
export let graph;
export let record;
export let isCreateMode;
export let activeContentTab;
function clone(e) {
e.preventDefault();
axios.post(channel.lucentUrl + "/records/clone/" + record.id).then(response => {
window.location = channel.lucentUrl + "/records/" + response.data.id;
}).catch(error => {
});
}
</script>
<h3 class="header-normal mt-5 mb-0">
<a
class="text-muted d-block text-decoration-none fs-6 mb-1"
href="{channel.lucentUrl}/content/{schema.name}"
>{schema.label.toUpperCase()}</a
>
<span class="text-dark d-block">
{#if !isCreateMode}
{previewTitle(channel.schemas, record, graph)}
{:else}
New Record
{/if}
</span>
{#if !isCreateMode}
<div class="dropdown d-inline-block">
<button
class="btn btn-link btn-sm"
type="button"
data-bs-toggle="dropdown"
aria-expanded="false"
>
<Icon icon="ellipsis"/>
</button>
<div class="dropdown-menu">
<h6 class="dropdown-header">Record Actions</h6>
<a
class="dropdown-item"
href="{channel.lucentUrl}/records/new?schema={schema.name}"
>Create new</a
>
{#if !isCreateMode}
<a
class="dropdown-item"
on:click={clone}
href={channel.lucentUrl}
>
Clone
</a>
{/if}
<a
on:click|preventDefault={(e) =>
(activeContentTab = "_info")}
class="dropdown-item"
href="{channel.lucentUrl}">Revisions</a
>
</div>
</div>
{/if}
</h3>
+2 -2
View File
@@ -47,8 +47,8 @@
const id = `field-${field.name}-${record.id}`;
</script>
<div class="card editor-field">
<FieldHeader {schema} {field} {id}/>
<div class="editor-field">
<FieldHeader {field} {id}/>
{#if field.info.name === "reference" && field.layout === "inline"}
<ReferenceInline
bind:graph
+2 -2
View File
@@ -4,8 +4,8 @@
import {isEqual} from "lodash";
import FormField from "./FormField.svelte";
import FilePreview from "./FilePreview.svelte";
import ContentTabs from "./ContentTabs.svelte";
import StatusSelect from "./StatusSelect.svelte";
import ContentTabs from "./header/ContentTabs.svelte";
import StatusSelect from "./header/StatusSelect.svelte";
import ErrorAlert from "../common/ErrorAlert.svelte";
const channel = getContext("channel");
@@ -1,57 +0,0 @@
<script>
import {getContext} from "svelte";
import {getStatus, getStatusList} from "./StatusText";
const channel = getContext("channel");
export let status = "draft";
export let record;
export let schema;
let dropdown;
$: currentStatus = getStatus(status);
const statusList = Object.values(getStatusList());
function updateStatus(e, statusValue) {
// e.preventDefault();
status = statusValue;
dropdown.click();
}
</script>
<!-- Example split danger button -->
<div class="d-flex justify-content-between">
<div class="btn-group dropup">
<button type="button" class="btn btn-{currentStatus.bg}"
>{currentStatus.text}</button
>
<button
bind:this={dropdown}
type="button"
class="btn btn-{currentStatus.bg} dropdown-toggle dropdown-toggle-split"
data-bs-toggle="dropdown"
aria-expanded="false"
>
<span class="visually-hidden">Toggle Dropdown</span>
</button>
<div class="dropdown-menu">
<div class="dropdown-header">Change status to</div>
{#each statusList as astatus}
{#if astatus.value !== status}
<button
type="button"
class="dropdown-item my-2 rounded w-100 bg-{astatus.bg} text-{astatus.color}"
on:click={(e) => updateStatus(e, astatus.value)}
>
{astatus.text}
</button>
{/if}
{/each}
</div>
</div>
{#if channel.previewTarget}
<a href="{channel.previewTargetUrl}?schema={schema.name}&id={record.id}" target="_blank" class="btn btn-info ms-3">
Preview
</a>
{/if}
</div>
@@ -8,7 +8,7 @@
$: errorMessage = getErrorMessage(validationErrors, field.name);
</script>
<div>
<div class="field-checkbox">
<div class="form-check form-check-inline">
<input
class="form-check-input"
@@ -3,20 +3,20 @@
export let id;
</script>
<div class="mb-1">
<div class="d-flex justify-content-between">
<div>
<label for={id} class="form-label"
<div class="field-header">
<div class="labels">
<div class="label-and-help">
<label for={id}
>{field.label}</label
>
{#if field.help}
<small class=" text-primary opacity-50">{field.help}</small>
<small class="help-text">{field.help}</small>
{/if}
</div>
<span
tabindex="-1"
class="text-decoration-none"
><code class="text-primary opacity-50">{field.name}</code>
><code class="field-id">{field.name}</code>
</span>
</div>
</div>
@@ -37,12 +37,12 @@
</script>
{#if tabs.length > 1}
<ul class="nav nav-pills mb-4 justify-content-center">
<ul class="tabs">
{#each tabs as tab}
<li class="nav-item">
<li class="tab">
<button
on:click={(e) => changeTab(e, tab.name)}
class="nav-link"
class="button"
class:active={active === tab.name}
aria-current="page"
>
@@ -0,0 +1,54 @@
<script>
import {getContext} from "svelte";
import Icon from "../../common/Icon.svelte";
import Dropdown from "../../common/Dropdown.svelte";
import StatusSelect from "./StatusSelect.svelte";
const channel = getContext("channel");
export let schema;
export let record;
export let isCreateMode;
export let activeContentTab;
function clone(e) {
e.preventDefault();
axios.post(channel.lucentUrl + "/records/clone/" + record.id).then(response => {
window.location = channel.lucentUrl + "/records/" + response.data.id;
}).catch(error => {
});
}
</script>
<StatusSelect bind:status={record.status} {record}></StatusSelect>
{#if !isCreateMode}
<Dropdown orientation="right">
<div slot="button">
<Icon icon="ellipsis"/>
</div>
<h6 class="dropdown-header">Record Actions</h6>
<a
class="dropdown-item"
href="{channel.lucentUrl}/records/new?schema={schema.name}"
>Create new</a
>
{#if !isCreateMode}
<a
class="dropdown-item"
on:click={clone}
href={channel.lucentUrl}
>
Clone
</a>
{/if}
<a
on:click|preventDefault={(e) =>
(activeContentTab = "_info")}
class="dropdown-item"
href="{channel.lucentUrl}">Revisions</a
>
</Dropdown>
{/if}
@@ -0,0 +1,26 @@
<script>
import Switch from "../../common/Switch.svelte";
export let status = "draft";
export let record;
function updateStatus(e) {
if(e.target.checked){
status = "published";
}else{
status = "draft";
}
}
</script>
{#if record.status !== "trashed"}
<Switch value="published" on:change={updateStatus} checked={record.status === "published"}></Switch>
{/if}
{#if record.status === "published"}
Published
{:else if record.status === "draft"}
Draft
{:else if record.status === "trashed"}
Trashed
{/if}
@@ -0,0 +1,27 @@
<script>
import {getContext} from "svelte";
import {previewTitle} from "./../Preview";
const channel = getContext("channel");
export let schema;
export let record;
export let isCreateMode;
</script>
<div class="record-header">
<span class="record-title">
{#if !isCreateMode}
{previewTitle(channel.schemas, record)}
{:else}
New Record
{/if}
</span>
<a
class="schema-name"
href="{channel.lucentUrl}/content/{schema.name}"
>{schema.label.toUpperCase()}</a
>
</div>