schemas and fields
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<script>
|
||||
import { formatDistanceToNow, parseJSON } from "date-fns";
|
||||
import Avatar from "../../svelte/account/Avatar.svelte";
|
||||
import Avatar from "../../common/Avatar.svelte";
|
||||
import { previewTitle } from "../../svelte/records/Preview";
|
||||
import Preview from "../../svelte/files/Preview.svelte";
|
||||
import { usernameById } from "../../svelte/account/users";
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
<script>
|
||||
import ChannelLayout from "../../layouts/ChannelLayout.svelte";
|
||||
import { post } from "../../modules/remote";
|
||||
let { channel, user, data } = $props();
|
||||
let newSchemaName = $state("");
|
||||
let newSchemaAlias = $state("");
|
||||
|
||||
function handleSchemaCreate(e) {
|
||||
e.preventDefault();
|
||||
post(
|
||||
channel.lucentUrl + "/schemas",
|
||||
{
|
||||
name: newSchemaName,
|
||||
alias: newSchemaAlias,
|
||||
},
|
||||
(data, err) => {
|
||||
if (err.isEmpty()) {
|
||||
window.location.reload();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
</script>
|
||||
|
||||
<ChannelLayout {body} {channel} {user}></ChannelLayout>
|
||||
{#snippet body()}
|
||||
<h3 class="header-small mb-4 mt-5">Schemas</h3>
|
||||
|
||||
<details style="max-width: 400px;">
|
||||
<summary>Create Schema</summary>
|
||||
<form onsubmit={handleSchemaCreate}>
|
||||
<fieldset>
|
||||
<label>
|
||||
Name
|
||||
<input
|
||||
bind:value={newSchemaName}
|
||||
placeholder="Schema name"
|
||||
minlength="2"
|
||||
maxlength="30"
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Alias
|
||||
<input
|
||||
bind:value={newSchemaAlias}
|
||||
placeholder="Schema alias"
|
||||
minlength="2"
|
||||
maxlength="30"
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
</fieldset>
|
||||
|
||||
<button type="submit">Create</button>
|
||||
</form>
|
||||
</details>
|
||||
<div style="display: flex;gap:20px">
|
||||
{#each data.schemas as schema}
|
||||
<article style="min-width: 300px;">
|
||||
<header>{schema.name}</header>
|
||||
<details>
|
||||
<summary>Fields</summary>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Title</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</details>
|
||||
<details>
|
||||
<summary>Add field</summary>
|
||||
<ul>
|
||||
<li>
|
||||
<a
|
||||
href={`/fields/create?schema=${schema.id}&type=text`}
|
||||
>Text</a
|
||||
>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a
|
||||
href={`/fields/create?schema=${schema.id}&type=text`}
|
||||
>File</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href={`/fields/create?schema=${schema.id}&type=text`}
|
||||
>Rich</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</details>
|
||||
<footer>
|
||||
<small> Id: {schema.id}</small><br />
|
||||
<small> Alias: {schema.alias}</small><br />
|
||||
<small> Revisions: {schema.revisions}</small><br />
|
||||
</footer>
|
||||
</article>
|
||||
{/each}
|
||||
</div>
|
||||
{/snippet}
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
<div class="top-nav">
|
||||
<a class="top-nav-item" href="{channel.lucentUrl}/members">Members</a>
|
||||
<a class="top-nav-item" href="{channel.lucentUrl}/schemas">Schemas</a>
|
||||
|
||||
<!-- {#if channel.commands.length > 0}
|
||||
<Dropdown>
|
||||
|
||||
@@ -13,6 +13,7 @@ import RecordNotFound from "./svelte/records/NotFound.svelte";
|
||||
import RecordEdit from "./svelte/records/Edit.svelte";
|
||||
import ContentIndex from "./svelte/content/Index.svelte";
|
||||
import HomeEntry from "./entry/HomeEntry/HomeEntry.svelte";
|
||||
import SchemaEntry from "./entry/SchemaEntry/SchemaEntry.svelte";
|
||||
import BuildReport from "./svelte/build/Report.svelte";
|
||||
|
||||
const entryComponents = {
|
||||
@@ -27,6 +28,7 @@ const entryComponents = {
|
||||
verify: Verify,
|
||||
profile: Profile,
|
||||
setup: SetupIndex,
|
||||
schemas: SchemaEntry,
|
||||
};
|
||||
|
||||
let loadedComponents = [];
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script>
|
||||
import ErrorAlert from "../common/ErrorAlert.svelte";
|
||||
import SpinnerButton from "../common/SpinnerButton.svelte";
|
||||
import Avatar from "./Avatar.svelte";
|
||||
import {getContext} from "svelte";
|
||||
import Avatar from "./../../common/Avatar.svelte";
|
||||
import { getContext } from "svelte";
|
||||
import SuccessAlert from "../common/SuccessAlert.svelte";
|
||||
|
||||
const user = getContext("user");
|
||||
@@ -25,7 +25,7 @@
|
||||
})
|
||||
.catch((error) => {
|
||||
errorMessage = error.response?.data.error;
|
||||
console.log({errorMessage});
|
||||
console.log({ errorMessage });
|
||||
});
|
||||
}
|
||||
|
||||
@@ -42,46 +42,47 @@
|
||||
})
|
||||
.catch((error) => {
|
||||
errorMessage = error.response?.data.error;
|
||||
console.log({errorMessage});
|
||||
console.log({ errorMessage });
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<div class="wrapper-tiny">
|
||||
<ErrorAlert message={errorMessage}/>
|
||||
<ErrorAlert message={errorMessage} />
|
||||
<SuccessAlert bind:this={successAlert} />
|
||||
<h3 class="header-small mb-5">
|
||||
<Avatar name={user.name}/>
|
||||
<Avatar name={user.name} />
|
||||
</h3>
|
||||
<form on:submit={saveName}>
|
||||
<div class="input-group mb-5">
|
||||
<input
|
||||
type="text"
|
||||
bind:value={name}
|
||||
class="form-control mb-3"
|
||||
placeholder="Name"
|
||||
required
|
||||
type="text"
|
||||
bind:value={name}
|
||||
class="form-control mb-3"
|
||||
placeholder="Name"
|
||||
required
|
||||
/>
|
||||
<SpinnerButton label="Update Name"/>
|
||||
<SpinnerButton label="Update Name" />
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<form on:submit={saveEmail}>
|
||||
<div class="input-group mb-5">
|
||||
<input
|
||||
type="email"
|
||||
bind:value={email}
|
||||
class="form-control mb-3"
|
||||
placeholder="Email"
|
||||
required
|
||||
type="email"
|
||||
bind:value={email}
|
||||
class="form-control mb-3"
|
||||
placeholder="Email"
|
||||
required
|
||||
/>
|
||||
<SpinnerButton label="Update Email"/>
|
||||
<SpinnerButton label="Update Email" />
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="list-group">
|
||||
<a class="list-group-item list-group-item-action" href="{ channel.lucentUrl }/logout">Logout from this
|
||||
device</a>
|
||||
<a
|
||||
class="list-group-item list-group-item-action"
|
||||
href="{channel.lucentUrl}/logout">Logout from this device</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script>
|
||||
import RenderField from "./RenderField.svelte";
|
||||
import Avatar from "../account/Avatar.svelte";
|
||||
import Avatar from "../../common/Avatar.svelte";
|
||||
import Status from "../records/Status.svelte";
|
||||
import {usernameById} from "../account/users";
|
||||
import {friendlyDate} from "../../helpers";
|
||||
import { usernameById } from "../account/users";
|
||||
import { friendlyDate } from "../../helpers";
|
||||
|
||||
export let schema;
|
||||
export let users;
|
||||
@@ -12,7 +12,6 @@
|
||||
export let sortParam;
|
||||
export let sortField;
|
||||
export let visibleColumns;
|
||||
|
||||
</script>
|
||||
|
||||
{#each visibleColumns as field, index}
|
||||
@@ -20,7 +19,7 @@
|
||||
class="field-ui-{field.info.name}"
|
||||
class:is-sort={field.name === sortField.name}
|
||||
>
|
||||
<RenderField {record} {schema} {graph} {field}/>
|
||||
<RenderField {record} {schema} {graph} {field} />
|
||||
</td>
|
||||
{/each}
|
||||
{#if schema.visible?.includes("status")}
|
||||
@@ -28,32 +27,40 @@
|
||||
class="text-center"
|
||||
class:is-sort={"-status" == sortParam || "status" == sortParam}
|
||||
>
|
||||
<Status status={record.status}/>
|
||||
<Status status={record.status} />
|
||||
</td>
|
||||
{/if}
|
||||
{#if schema.visible?.includes("_sys.createdBy")}
|
||||
<td
|
||||
class="text-center"
|
||||
class:is-sort={"-_sys.createdBy" == sortParam || "_sys.createdBy" == sortParam}
|
||||
class:is-sort={"-_sys.createdBy" == sortParam ||
|
||||
"_sys.createdBy" == sortParam}
|
||||
>
|
||||
<Avatar name={usernameById(users, record._sys.createdBy)} side={24}/>
|
||||
<Avatar name={usernameById(users, record._sys.createdBy)} side={24} />
|
||||
</td>
|
||||
{/if}
|
||||
{#if schema.visible?.includes("_sys.updatedBy")}
|
||||
<td
|
||||
class="text-center"
|
||||
class:is-sort={"-_sys.updatedBy" == sortParam || "_sys.updatedBy" == sortParam}
|
||||
class:is-sort={"-_sys.updatedBy" == sortParam ||
|
||||
"_sys.updatedBy" == sortParam}
|
||||
>
|
||||
<Avatar name={usernameById(users, record._sys.updatedBy)} side={24}/>
|
||||
<Avatar name={usernameById(users, record._sys.updatedBy)} side={24} />
|
||||
</td>
|
||||
{/if}
|
||||
{#if schema.visible?.includes("_sys.createdAt")}
|
||||
<td class:is-sort={"-_sys.createdAt" == sortParam || "_sys.createdAt" == sortParam}>
|
||||
<td
|
||||
class:is-sort={"-_sys.createdAt" == sortParam ||
|
||||
"_sys.createdAt" == sortParam}
|
||||
>
|
||||
{friendlyDate(record._sys.createdAt)}
|
||||
</td>
|
||||
{/if}
|
||||
{#if schema.visible?.includes("_sys.updatedAt")}
|
||||
<td class:is-sort={"-_sys.updatedAt" == sortParam || "_sys.updatedAt" == sortParam}>
|
||||
<td
|
||||
class:is-sort={"-_sys.updatedAt" == sortParam ||
|
||||
"_sys.updatedAt" == sortParam}
|
||||
>
|
||||
{friendlyDate(record._sys.updatedAt)}
|
||||
</td>
|
||||
{/if}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<script>
|
||||
import RecordRow from "./RecordRow.svelte";
|
||||
import {previewTitle} from "../records/Preview";
|
||||
import {usernameById} from "../account/users";
|
||||
import {getContext} from "svelte";
|
||||
import Avatar from "../account/Avatar.svelte";
|
||||
import {selectRecord, toggleAll} from "./functions/recordSelect.js";
|
||||
import { previewTitle } from "../records/Preview";
|
||||
import { usernameById } from "../account/users";
|
||||
import { getContext } from "svelte";
|
||||
import Avatar from "../../common/Avatar.svelte";
|
||||
import { selectRecord, toggleAll } from "./functions/recordSelect.js";
|
||||
import Checkbox from "../common/Checkbox.svelte";
|
||||
import Preview from "../files/Preview.svelte";
|
||||
import {fileurl} from "../files/imageserver.js";
|
||||
import { fileurl } from "../files/imageserver.js";
|
||||
|
||||
const channel = getContext("channel");
|
||||
|
||||
@@ -23,107 +23,134 @@
|
||||
export let selected = [];
|
||||
|
||||
function eventToggleAll(e) {
|
||||
selected = toggleAll(e, records, selected)
|
||||
selected = toggleAll(e, records, selected);
|
||||
}
|
||||
|
||||
function select(record) {
|
||||
selected = selectRecord(record, selected)
|
||||
selected = selectRecord(record, selected);
|
||||
}
|
||||
|
||||
$: visibleColumns = schema.fields.filter(c => schema.visible?.includes(c.name) ?? [])
|
||||
$: visibleColumns = schema.fields.filter(
|
||||
(c) => schema.visible?.includes(c.name) ?? [],
|
||||
);
|
||||
</script>
|
||||
|
||||
<div class="table mt-5 ">
|
||||
<div class="table mt-5">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
{#if isWritable}
|
||||
<th>
|
||||
<Checkbox
|
||||
<tr>
|
||||
{#if isWritable}
|
||||
<th>
|
||||
<Checkbox
|
||||
value=""
|
||||
on:change={eventToggleAll}
|
||||
indeterminate={selected.length > 0 && selected.length < records.length}
|
||||
indeterminate={selected.length > 0 &&
|
||||
selected.length < records.length}
|
||||
checked={selected.length === records.length}
|
||||
>
|
||||
</Checkbox>
|
||||
</th>
|
||||
{/if}
|
||||
></Checkbox>
|
||||
</th>
|
||||
{/if}
|
||||
|
||||
{#each visibleColumns as field}
|
||||
<th
|
||||
{#each visibleColumns as field}
|
||||
<th
|
||||
class="field-ui-{field.info.name ?? field.ui}"
|
||||
class:is-sort={field.name === sortField.name}
|
||||
scope="col"
|
||||
title={field.help}
|
||||
>{field.label}</th
|
||||
>
|
||||
{/each}
|
||||
{#each systemFields.filter(c => schema.visible?.includes(c.name)) as sysField}
|
||||
<th class:is-sort={sysField.name === sortField.name}>{sysField.label}</th>
|
||||
{/each}
|
||||
<th></th>
|
||||
</tr>
|
||||
title={field.help}>{field.label}</th
|
||||
>
|
||||
{/each}
|
||||
{#each systemFields.filter( (c) => schema.visible?.includes(c.name), ) as sysField}
|
||||
<th class:is-sort={sysField.name === sortField.name}
|
||||
>{sysField.label}</th
|
||||
>
|
||||
{/each}
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each records as record (record.id)}
|
||||
<tr>
|
||||
<td class="title-td">
|
||||
<div
|
||||
class="title-td-contents"
|
||||
>
|
||||
{#if isWritable}
|
||||
<Checkbox
|
||||
{#each records as record (record.id)}
|
||||
<tr>
|
||||
<td class="title-td">
|
||||
<div class="title-td-contents">
|
||||
{#if isWritable}
|
||||
<Checkbox
|
||||
on:change={() => select(record)}
|
||||
checked={selected.find((r) => r.id === record.id)}
|
||||
checked={selected.find(
|
||||
(r) => r.id === record.id,
|
||||
)}
|
||||
value={record}
|
||||
>
|
||||
</Checkbox>
|
||||
></Checkbox>
|
||||
{/if}
|
||||
{#if record._file?.path}
|
||||
<div class="file-table-row">
|
||||
<Preview
|
||||
{record}
|
||||
size={record._file?.width > 0
|
||||
? "medium"
|
||||
: "small"}
|
||||
/>
|
||||
|
||||
{/if}
|
||||
{#if record._file?.path}
|
||||
<div class="file-table-row">
|
||||
<Preview record={record} size={record._file?.width > 0 ? "medium" : "small"}/>
|
||||
|
||||
<div>
|
||||
{#if record.status === "draft"}
|
||||
<span style="text-transform: uppercase;font-size:10px">{record.status}</span>
|
||||
{/if}
|
||||
<a
|
||||
<div>
|
||||
{#if record.status === "draft"}
|
||||
<span
|
||||
style="text-transform: uppercase;font-size:10px"
|
||||
>{record.status}</span
|
||||
>
|
||||
{/if}
|
||||
<a
|
||||
href="{channel.lucentUrl}/records/{record.id}"
|
||||
target={inModal ? "_blank" : "_self"}
|
||||
>
|
||||
{previewTitle(channel.schemas, record, graph)}
|
||||
</a>
|
||||
<span>{(record._file.size / 1024).toFixed(1)}kB</span>
|
||||
target={inModal
|
||||
? "_blank"
|
||||
: "_self"}
|
||||
>
|
||||
{previewTitle(
|
||||
channel.schemas,
|
||||
record,
|
||||
graph,
|
||||
)}
|
||||
</a>
|
||||
<span
|
||||
>{(
|
||||
record._file.size / 1024
|
||||
).toFixed(1)}kB</span
|
||||
>
|
||||
|
||||
{#if record._file.width > 0}
|
||||
<span>{record._file.width + "x" + record._file.height}</span>
|
||||
{/if}
|
||||
<a
|
||||
href="{fileurl(channel,record)}"
|
||||
{#if record._file.width > 0}
|
||||
<span
|
||||
>{record._file.width +
|
||||
"x" +
|
||||
record._file.height}</span
|
||||
>
|
||||
{/if}
|
||||
<a
|
||||
href={fileurl(channel, record)}
|
||||
target="_blank"
|
||||
>
|
||||
Download
|
||||
</a>
|
||||
>
|
||||
Download
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{:else}
|
||||
<a
|
||||
{:else}
|
||||
<a
|
||||
href="{channel.lucentUrl}/records/{record.id}"
|
||||
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)}
|
||||
</a>
|
||||
{/if}
|
||||
|
||||
|
||||
</div>
|
||||
</td>
|
||||
<RecordRow
|
||||
>
|
||||
{#if record.status === "draft"}
|
||||
<span
|
||||
style="text-transform: uppercase;font-size:10px"
|
||||
>{record.status}</span
|
||||
>
|
||||
{/if}
|
||||
{previewTitle(
|
||||
channel.schemas,
|
||||
record,
|
||||
graph,
|
||||
)}
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
</td>
|
||||
<RecordRow
|
||||
{record}
|
||||
{graph}
|
||||
{schema}
|
||||
@@ -131,19 +158,15 @@
|
||||
{sortParam}
|
||||
{sortField}
|
||||
{users}
|
||||
/>
|
||||
<td>
|
||||
<Avatar
|
||||
name={usernameById(
|
||||
users,
|
||||
record._sys.updatedBy
|
||||
)}
|
||||
side={24}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
<td>
|
||||
<Avatar
|
||||
name={usernameById(users, record._sys.updatedBy)}
|
||||
side={24}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
<script>
|
||||
import Avatar from "../account/Avatar.svelte";
|
||||
import {fly} from "svelte/transition";
|
||||
import {createEventDispatcher} from "svelte";
|
||||
import Avatar from "../../common/Avatar.svelte";
|
||||
import { fly } from "svelte/transition";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import Dropdown from "../common/Dropdown.svelte";
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
export let member;
|
||||
export let roles;
|
||||
|
||||
|
||||
function removeFrom(e, aRole) {
|
||||
e.preventDefault();
|
||||
let newRoles = member.roles.filter((r) => r !== aRole);
|
||||
@@ -22,24 +21,23 @@
|
||||
e.preventDefault();
|
||||
|
||||
let newRoles = [...member.roles, aRole];
|
||||
console.log(member.roles)
|
||||
console.log(aRole)
|
||||
console.log(newRoles)
|
||||
console.log(member.roles);
|
||||
console.log(aRole);
|
||||
console.log(newRoles);
|
||||
dispatch("update", {
|
||||
user: member.id,
|
||||
roles: newRoles,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<div
|
||||
transition:fly={{ duration: 200 }}
|
||||
class="member-item"
|
||||
>
|
||||
<div class="member-name status-{member.roles.includes('removed') ? 'removed' : 'active'}">
|
||||
<Avatar name={member.name ?? "" } side={32}/>
|
||||
<div transition:fly={{ duration: 200 }} class="member-item">
|
||||
<div
|
||||
class="member-name status-{member.roles.includes('removed')
|
||||
? 'removed'
|
||||
: 'active'}"
|
||||
>
|
||||
<Avatar name={member.name ?? ""} side={32} />
|
||||
<div>
|
||||
<div>
|
||||
{member.name}
|
||||
@@ -50,41 +48,35 @@
|
||||
</div>
|
||||
</div>
|
||||
<Dropdown orientation="right">
|
||||
<div slot="button">
|
||||
Roles
|
||||
</div>
|
||||
<div slot="button">Roles</div>
|
||||
<h6 class="dropdown-header">Remove role</h6>
|
||||
{#each roles as role}
|
||||
{#if member.roles.includes(role)}
|
||||
<button
|
||||
class="dropdown-item button"
|
||||
on:click={(e) => removeFrom(e,role)}
|
||||
class="dropdown-item button"
|
||||
on:click={(e) => removeFrom(e, role)}
|
||||
>
|
||||
{role}
|
||||
</button>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
|
||||
<h6 class="dropdown-header">Add role</h6>
|
||||
{#each roles as role}
|
||||
{#if !member.roles.includes(role)}
|
||||
<button
|
||||
class="dropdown-item button"
|
||||
on:click={(e) => addTo(e,role)}
|
||||
class="dropdown-item button"
|
||||
on:click={(e) => addTo(e, role)}
|
||||
>
|
||||
{role}
|
||||
</button>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
</Dropdown>
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.status-removed {
|
||||
opacity: .5;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script>
|
||||
import { friendlyDate } from "../../helpers";
|
||||
import Avatar from "../account/Avatar.svelte";
|
||||
import Avatar from "../../common/Avatar.svelte";
|
||||
import { usernameById } from "../account/users";
|
||||
import Icon from "../common/Icon.svelte";
|
||||
import RevisionCell from "./revisions/RevisionCell.svelte";
|
||||
|
||||
Reference in New Issue
Block a user