content tables

This commit is contained in:
2026-01-12 19:25:40 +02:00
parent b0485e77fc
commit 178b9309ec
16 changed files with 483 additions and 23 deletions
@@ -1,6 +1,8 @@
<script>
import { post } from "../../modules/remote";
import { getApp } from "../../app";
import Table from "./Table.svelte";
import Tools from "./Tools.svelte";
// import Tools from "./tools/Tools.svelte";
// import Pagination from "./pagination/Pagination.svelte";
@@ -102,19 +104,9 @@
on:refresh={refresh}
/>
{/if}
<Table
{records}
{graph}
{schema}
{sortParam}
{sortField}
{systemFields}
{inModal}
{users}
{isWritable}
bind:selected
/> -->
-->
<Tools fields={data.fields}></Tools>
<Table records={data.records} fields={data.fields}></Table>
</div>
<!--
<Pagination
+120
View File
@@ -0,0 +1,120 @@
<script>
// import RecordRow from "./RecordRow.svelte";
// import { previewTitle } from "../records/Preview";
// import { usernameById } from "../account/users";
// 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 { getApp } from "../../app";
let { channel, records = [], fields = [], inModal = false } = $props();
let params = new URLSearchParams(document.location.search);
let columns = $state(params.get("columns")?.split(",") || []);
let visibleFields = $derived(fields.filter((f) => columns.includes(f.id)));
// export let schema;
// export let users;
// export let records;
// export let graph;
// export let systemFields;
// export let sortParam;
// export let sortField;
// export let inModal;
// export let isWritable;
// export let selected = [];
const app = getApp();
// function eventToggleAll(e) {
// selected = toggleAll(e, records, selected);
// }
// function select(record) {
// selected = selectRecord(record, selected);
// }
// $: visibleColumns = schema.fields.filter(
// (c) => schema.visible?.includes(c.name) ?? [],
// );
</script>
<div class="table mt-5">
<table>
<thead>
<tr>
<!-- <th>
<Checkbox
value=""
on:change={eventToggleAll}
indeterminate={selected.length > 0 &&
selected.length < records.length}
checked={selected.length === records.length}
></Checkbox>
</th> -->
<th>Title</th>
{#each visibleFields as field}
<th scope="col" title={field.help}>{field.name}</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 qRecord (qRecord.record.id)}
<tr>
<td class="title-td">
<div class="title-td-contents">
<!-- <Checkbox
on:change={() => select(record)}
checked={selected.find(
(r) => r.id === record.id,
)}
value={record}
></Checkbox> -->
<a
href="{app.url('records')}{qRecord.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)} -->
{qRecord.recordPreview.title}
</a>
</div>
</td>
{#each visibleFields as field}
<td>
{qRecord.data.find((f) => f.fieldId === field.id)
?.value}
</td>
{/each}
<!-- <RecordRow
{record}
{graph}
{schema}
{visibleColumns}
{sortParam}
{sortField}
{users}
/> -->
<!-- <td>
<Avatar
name={usernameById(users, record._sys.updatedBy)}
side={24}
/>
</td> -->
</tr>
{/each}
</tbody>
</table>
</div>
+76
View File
@@ -0,0 +1,76 @@
<script>
let { fields } = $props();
let params = new URLSearchParams(document.location.search);
let sortBy = $state(params.get("sortBy") + "_" + params.get("sortDir"));
let columns = $state(params.get("columns")?.split(",") || []);
function handleSortAsc() {
params.set("sortBy", sortBy.replace("_asc", ""));
params.set("sortDir", "asc");
Turbo.visit(window.location.pathname + `?` + params.toString());
}
function handleSortDesc() {
params.set("sortBy", sortBy.replace("_desc", ""));
params.set("sortDir", "desc");
Turbo.visit(window.location.pathname + `?` + params.toString());
}
function handleToggleColumn() {
params.set("columns", columns.join(","));
Turbo.visit(window.location.pathname + `?` + params.toString());
}
</script>
<!-- Radios -->
<details class="dropdown">
<summary> Sort by </summary>
<ul>
{#each fields as field}
<li>
<label>
<input
bind:group={sortBy}
type="radio"
value={field.id + "_asc"}
onchange={handleSortAsc}
/>
{field.name}
</label>
</li>
<li>
<label>
<input
bind:group={sortBy}
type="radio"
value={field.id + "_desc"}
onchange={handleSortDesc}
/>
{field.name} desc
</label>
</li>
{/each}
</ul>
</details>
<!-- Checkboxes -->
<details class="dropdown">
<summary> Show/Hide columns </summary>
<ul>
{#each fields as field}
<li>
<label>
<input
bind:group={columns}
type="checkbox"
value={field.id}
onchange={handleToggleColumn}
/>
{field.name}
</label>
</li>
{/each}
</ul>
</details>
@@ -15,6 +15,12 @@
function handleLocaleChange() {
selectedLocales = getSelectedLocales();
}
function toggleLiveData() {
if (!showPublished) {
// to avoid state sync
Turbo.visit(window.location.href);
}
}
</script>
<!-- <svelte:window on:beforeunload={beforeUnload} /> -->
@@ -28,6 +34,7 @@
<label>
<input
bind:checked={showPublished}
onchange={toggleLiveData}
type="checkbox"
role="switch"
/>
@@ -20,7 +20,6 @@
(f) => f.fieldId === field.id && f.locale === locale,
);
};
$inspect(edgeRecordPreviews);
const findFieldEdges = (field, locale) => {
return edgeRecordPreviews.filter(
(e) => e.edge.fieldId === field.id && e.edge.locale === locale,
@@ -57,7 +57,7 @@
(data, err) => {
suggestionsLoaded = true;
dialog.close();
edgeRecordPreviews = [...edgeRecordPreviews, ...data];
edgeRecordPreviews = data;
},
);
}
+14
View File
@@ -0,0 +1,14 @@
<?php namespace Lucent\Core\Data;
class QueryRecord
{
/**
* @param RecordField[] $data
*/
public function __construct(
public ?Edge $edge,
public Record $record,
public RecordPreview $recordPreview,
public array $data,
) {}
}
+116
View File
@@ -0,0 +1,116 @@
<?php namespace Lucent\Core\Query;
use Illuminate\Support\Facades\DB;
use Lucent\Core\Data\RecordPreview;
use Lucent\Core\Data\QueryRecord;
use Lucent\Core\Data\RecordMode;
use Lucent\Core\Record\RecordModule;
use Lucent\Core\Record\RecordFieldModule;
use stdClass;
class QueryModule
{
public static function withSchema($query, string $schemaId)
{
return $query->where("records.schema_id", "=", $schemaId);
}
public static function withSort($query, string $fieldId, string $sortDir)
{
return $query
->join("records_data as sortData", function ($join) use (
$fieldId,
$sortDir,
) {
$join
->on("records.id", "=", "sortData.record_id")
->where("sortData.mode", "=", RecordMode::DRAFT)
->where("sortData.locale", "=", "main")
->where("sortData.field_id", "=", $fieldId);
})
->orderBy("sortData.value", $sortDir)
->distinct();
}
// public static function withColumns($query, array $columns)
// {
// return $query
// ->join("records_data as includeData", function ($join) use (
// $columns,
// ) {
// $join
// ->on("records.id", "=", "includeData.record_id")
// ->where("includeData.mode", "=", RecordMode::DRAFT)
// ->where("includeData.locale", "=", "main")
// ->whereIn("includeData.field_id", $columns);
// })
// ->distinct();
// }
public static function run($query, array $schemas, array $columns)
{
$records = $query->get();
$recordIds = $records->pluck("id")->toArray();
$recordData = DB::table("records_data")
->whereIn("record_id", $recordIds)
->whereIn("field_id", $columns)
->where("mode", "=", RecordMode::DRAFT)
->where("locale", "=", "main")
->get()
->map(RecordFieldModule::fromDb(...))
->toArray();
return $records->map(
fn($row) => static::fromDB($row, $schemas, $recordData),
);
}
/** @return QueryRecord[] */
public static function make()
{
return DB::table("records")
->select("records.*", "records_data.value as record_title")
->join("records_data", function ($join) {
$join
->on("records.id", "=", "records_data.record_id")
->on(
"records.title_field_id",
"=",
"records_data.field_id",
);
})
->where("records_data.mode", "=", RecordMode::DRAFT)
->where("records_data.locale", "=", "main");
// ->leftJoin(
// "records_data",
// "records.id",
// "=",
// "records_data.record_id",
// )
}
/** @param array $data */
private static function fromDB(
stdClass $row,
$schemas,
array $recordData,
): QueryRecord {
return new QueryRecord(
null,
RecordModule::fromDb($row),
new RecordPreview(
id: $row->id,
schemaId: $row->schema_id,
schemaName: collect($schemas)
->where("id", data_get($row, "schema_id"))
->first()->name,
title: $row->record_title,
),
collect($recordData)
->where("recordId", $row->id)
->values()
->toArray(),
);
}
}
+22 -2
View File
@@ -3,8 +3,11 @@
use Carbon\Carbon;
use Lucent\Core\Data\RecordField;
use Lucent\Core\Data\RecordMode;
use Lucent\Core\Data\Edge;
use Lucent\Core\Repository\EdgeRepo;
use Lucent\Core\Repository\RecordFieldRepo;
use stdClass;
use Lucent\Id\Id;
use Illuminate\Support\Facades\DB;
class RecordFieldModule
@@ -16,20 +19,37 @@ class RecordFieldModule
->where("mode", RecordMode::DRAFT)
->values()
->map(function (RecordField $f) {
$f->id = Id::new();
$f->mode = RecordMode::LIVE;
return $f;
})
->toArray();
DB::transaction(function () use ($liveData, $recordId) {
$edges = EdgeRepo::findByRecordId($recordId);
$liveEdges = collect($edges)
->where("mode", RecordMode::DRAFT)
->values()
->map(function (Edge $e) {
$e->id = Id::new();
$e->mode = RecordMode::LIVE;
return $e;
})
->toArray();
DB::transaction(function () use ($liveData, $liveEdges, $recordId) {
RecordFieldRepo::deleteLiveByRecordId($recordId);
RecordFieldRepo::insertMany($liveData);
EdgeRepo::deleteLiveByRecordId($recordId);
EdgeRepo::insertMany($liveEdges);
});
}
public static function unpublish(string $recordId): void
{
RecordFieldRepo::deleteLiveByRecordId($recordId);
DB::transaction(function () use ($recordId) {
RecordFieldRepo::deleteLiveByRecordId($recordId);
EdgeRepo::deleteLiveByRecordId($recordId);
});
}
public static function fromDb(stdClass $field): RecordField
+38
View File
@@ -2,6 +2,7 @@
use Illuminate\Support\Facades\DB;
use Lucent\Core\Data\Edge;
use Lucent\Core\Data\RecordMode;
use Lucent\Core\Edge\EdgeModule;
class EdgeRepo
@@ -19,4 +20,41 @@ class EdgeRepo
array_map(EdgeModule::toDb(...), $edges),
);
}
public static function deleteLiveByRecordId(string $recordId): void
{
DB::table(self::TABLE_NAME)
->where("from", $recordId)
->where("mode", RecordMode::LIVE->value)
->delete();
}
/**
* @return Edge[]
*/
public static function findByRecordId(string $from): array
{
return DB::table(self::TABLE_NAME)
->where("from", $from)
->get()
->map(EdgeModule::fromDb(...))
->toArray();
}
public static function findLastOfField(
string $from,
string $fieldId,
string $locale,
): ?Edge {
return DB::table(self::TABLE_NAME)
->where("from", $from)
->where("field_id", $fieldId)
->where("locale", $locale)
->where("mode", RecordMode::DRAFT->value)
->orderBy("rank", "desc")
->limit(1)
->get()
->map(EdgeModule::fromDb(...))
->first();
}
}
+45 -1
View File
@@ -3,6 +3,7 @@
use Illuminate\Support\Facades\DB;
use Lucent\Core\Data\Record;
use Lucent\Core\Data\RecordPreview;
use Lucent\Core\Data\RecordMode;
use Lucent\Core\Data\EdgeRecordPreview;
use Lucent\Core\Record\RecordModule;
@@ -77,7 +78,50 @@ class RecordRepo
"records_data.value as record_title",
)
->where("edges.from", $recordId)
->join("records", "edges.from", "=", "records.id")
->join("records", "edges.to", "=", "records.id")
->join("records_data", function ($join) {
$join
->on("records.id", "=", "records_data.record_id")
->on(
"records.title_field_id",
"=",
"records_data.field_id",
);
})
->orderBy("edges.rank", "asc")
->get()
->map(
fn($row) => RecordModule::edgeRecordPreviewFromDb(
$schemas,
$row,
),
)
->toArray();
}
/**
* @param Schema[] $schemas
* @return EdgeRecordPreview[]
*/
public static function findEdgeRecordPreviewsForField(
array $schemas,
string $fieldId,
string $locale,
string $recordId,
): array {
return DB::table("edges")
->select(
"edges.*",
"records.id as record_id",
"records.schema_id as record_schema_id",
"records_data.value as record_title",
)
->where("edges.from", $recordId)
->where("edges.field_id", $fieldId)
->where("edges.locale", $locale)
->where("edges.mode", RecordMode::DRAFT)
->join("records", "edges.to", "=", "records.id")
->join("records_data", function ($join) {
$join
->on("records.id", "=", "records_data.record_id")
+1
View File
@@ -12,6 +12,7 @@ class Schema
public string $id,
public string $alias,
public string $name,
public string $titleId,
public int $revisions,
) {}
}
+3
View File
@@ -11,6 +11,7 @@ class SchemaModule
id: $data["id"],
alias: $data["alias"],
name: $data["label"],
titleId: $data["title_id"],
revisions: $data["revisions"],
);
}
@@ -21,6 +22,7 @@ class SchemaModule
id: data_get($data, "id"),
alias: data_get($data, "alias"),
name: data_get($data, "name"),
titleId: data_get($data, "title_id"),
revisions: data_get($data, "revisions"),
);
}
@@ -31,6 +33,7 @@ class SchemaModule
"id" => $schema->id,
"alias" => $schema->alias,
"name" => $schema->name,
"title_id" => $schema->titleId,
"revisions" => $schema->revisions,
];
}
+15 -2
View File
@@ -5,6 +5,8 @@ namespace Lucent\Http\Controller;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Lucent\Core\Repository\EdgeRepo;
use Lucent\Core\Repository\SchemaRepo;
use Lucent\Core\Repository\RecordRepo;
use Lucent\Core\Data\Edge;
use Lucent\Core\Data\RecordMode;
use Lucent\Id\Id;
@@ -20,6 +22,9 @@ class EdgeController extends Controller
$fieldId = $request->input("fieldId");
$locale = $request->input("locale");
$last = EdgeRepo::findLastOfField($from, $fieldId, $locale);
$lastRank = $last->rank ?? 0;
$edges = array_map(
fn($to) => new Edge(
id: Id::new(),
@@ -27,13 +32,21 @@ class EdgeController extends Controller
to: $to,
fieldId: $fieldId,
locale: $locale,
rank: 0,
rank: $lastRank + 1,
mode: RecordMode::DRAFT,
),
$toIds,
);
EdgeRepo::insertMany($edges);
return ok($edges);
$schemas = SchemaRepo::all();
$edgeRecordPreviews = RecordRepo::findEdgeRecordPreviewsForField(
$schemas,
$fieldId,
$locale,
$from,
);
return ok($edgeRecordPreviews);
}
}
+16 -1
View File
@@ -8,6 +8,7 @@ use Lucent\Account\AccountService;
use Lucent\Channel\ChannelService;
use Lucent\Core\Auth\AuthModule;
use Lucent\Core\Data\RecordField;
use Lucent\Core\Query\QueryModule;
use Lucent\Core\Repository\FieldRepo;
use Lucent\Core\Repository\SchemaRepo;
use Lucent\Core\Repository\RecordFieldRepo;
@@ -54,6 +55,14 @@ class RecordController
$schemaId = $request->route("id");
$schemas = SchemaRepo::all();
$schema = collect($schemas)->firstWhere("id", $schemaId);
$sortBy = $request->input("sortBy") ?? $schema->titleId;
$sortDir = $request->input("sortDir") ?? "asc";
$columns = empty($request->input("columns"))
? []
: explode(",", $request->input("columns"));
$fields = FieldRepo::findBySchemaId($schemaId);
// $users = $this->accountService->all();
$users = [];
// $schema = $this->channelService->getSchema($schemaName)->get();
@@ -72,7 +81,11 @@ class RecordController
// $skip = data_get($urlParams, "skip") ?? 0;
$skip = 0;
$limit = 30;
$records = [];
$query = QueryModule::make();
$query = QueryModule::withSchema($query, $schema->id);
$query = QueryModule::withSort($query, $sortBy, $sortDir);
$records = QueryModule::run($query, $schemas, $columns);
// dd($records);
// $graphArray = null;
// $graph = $this->query
@@ -94,6 +107,7 @@ class RecordController
"schema" => $schema,
"users" => $users,
"records" => $records,
"fields" => $fields,
// "graph" => toArray($graph),
// "systemFields" => array_values(System::list()),
// "operators" => $this->operatorRegistry->all(),
@@ -440,6 +454,7 @@ class RecordController
$record->scheduledAt = null;
$record->scheduledBy = null;
RecordFieldModule::unpublish($record->id);
RecordRepo::update($record);
return ok(toArray($record));
+4 -2
View File
@@ -39,17 +39,18 @@ class SchemaController
if ($validator->fails()) {
return response()->json(["errors" => $validator->errors()], 422);
}
$titleId = Id::new();
$schema = new Schema(
id: Id::new(),
alias: $request->input("alias"),
name: $request->input("name"),
titleId: $titleId,
revisions: 0,
);
SchemaRepo::insert($schema);
$field = new Field(
id: Id::new(),
id: $titleId,
schemaId: $schema->id,
name: "Title",
alias: "_title",
@@ -108,6 +109,7 @@ class SchemaController
id: $schema->id,
alias: $request->input("alias"),
name: $request->input("name"),
titleId: $schema->titleId,
revisions: $request->input("revisions"),
);