This commit is contained in:
2026-01-13 17:51:19 +02:00
parent 25d3b525f6
commit 268c696d64
25 changed files with 889 additions and 80 deletions
@@ -0,0 +1,10 @@
<script>
let { schemaField, validationError } = $props();
let hasError = $derived(!!validationError);
</script>
{#if hasError}
<small id={schemaField.id + "-help"}>{validationError.message}</small>
{:else if schemaField.help != ""}
<small id={schemaField.id + "-help"}>{schemaField.help}</small>
{/if}
@@ -0,0 +1,9 @@
<script>
import { getLocaleName } from "../locale.svelte.js";
let { channel, schemaField, locale } = $props();
</script>
{#if locale !== "main"}
{getLocaleName(channel, locale)} >
{/if}
{schemaField.name} <br />
@@ -0,0 +1,226 @@
<script>
import { get, post } from "../../../modules/remote";
import { uploadFile } from "../../../modules/upload";
import { getApp } from "../../../app";
import FieldLabel from "./FieldLabel.svelte";
import FieldError from "./FieldError.svelte";
import Sortable from "../../../common/Sortable.svelte";
let {
channel,
record,
schemaField,
dataField,
locale,
validationError,
edgeRecordPreviews,
} = $props();
let originalValue = dataField?.value ?? schemaField.props.default;
let newValue = $state(originalValue);
let valuesChanged = $derived(newValue !== originalValue);
let errorMessage = $state("");
let filesInProgress = $state([]);
let uploadInProgress = $derived(filesInProgress.length > 0);
// let validationErrorState = $state(validationError);
const app = getApp();
let suggestionsLoaded = $state(false);
let suggestions = $state([]);
let selectedRecordIds = $state([]);
let dialog = $state();
function handleModalOpen(e) {
// Add logic to handle adding a record
dialog.showModal();
if (suggestionsLoaded) {
return;
}
// get(app.url("records/files"), { recordId: record.id }, (data, err) => {
suggestionsLoaded = true;
// suggestions = data;
// });
}
function handleModalClose(e) {
dialog.close();
}
function handleInsertSelected() {
suggestionsLoaded = false;
post(
app.url("edges/many"),
{
toIds: selectedRecordIds,
from: record.id,
fieldId: schemaField.id,
locale: locale,
},
(data, err) => {
suggestionsLoaded = true;
dialog.close();
edgeRecordPreviews = data.edgeRecordPreviews;
validationError = data.validationError;
},
);
}
function handleSortUpdate(updatedEdges) {
// let updatedFieldIds = updatedFields.map((f) => f.id);
// fields = fields.filter((f) => !updatedFieldIds.includes(f.id));
// fields = [...fields, ...updatedFields];
post(
app.url("records/sort-edges"),
{
ids: updatedEdges.map((e) => e.edge.id),
},
(data, err) => {
edgeRecordPreviews = updatedEdges;
},
);
}
function handleRemoveEdge(edgeId) {
post(
app.url("edges/delete"),
{
id: edgeId,
from: record.id,
fieldId: schemaField.id,
locale: locale,
},
(data, err) => {
edgeRecordPreviews = edgeRecordPreviews.filter(
(e) => e.edge.id !== edgeId,
);
validationError = data.validationError;
},
);
}
function handleFilesUpload(e) {
let files = e.target.files ? [...e.target.files] : [];
let filesUploaded = files.map((file) => {
let fileInProgress = {
pct: 0,
hasFailed: false,
name: file.name,
};
filesInProgress.push(fileInProgress);
const progress = ({ pct, isComplete }) => {
filesInProgress.find((f) => f.name === file.name).pct = pct;
if (isComplete) {
filesInProgress = filesInProgress.filter(
(f) => f.name !== file.name,
);
}
};
const error = (errorMessage) => {
filesInProgress.find((f) => f.name === file.name).hasFailed =
true;
};
uploadFile(
file,
record.id,
schemaField.id,
locale,
progress,
error,
);
});
}
</script>
<div style="min-width: 400px;">
<label>
<FieldLabel {locale} {channel} {schemaField}></FieldLabel>
</label>
<FieldError {schemaField} {validationError}></FieldError>
<button onclick={handleModalOpen}>Choose files</button>
<dialog bind:this={dialog}>
<article>
<header>
<button onclick={handleModalClose} aria-label="Close" rel="prev"
></button>
<p>
<strong>Records</strong>
</p>
</header>
{#if suggestionsLoaded}
<form>
<button onclick={handleInsertSelected}>
Insert selected
</button>
<input
oninput={handleFilesUpload}
type="file"
multiple
disabled={uploadInProgress}
/>
{#each filesInProgress as fileInProgress}
<div>
<span>{fileInProgress.name}</span>
<progress value={fileInProgress.pct} max="100" />
{#if fileInProgress.hasFailed}
<span>Error</span>
{/if}
</div>
{/each}
<table>
<tbody>
{#each suggestions as suggestion}
<tr>
<td>
<input
type="checkbox"
value={suggestion.id}
bind:group={selectedRecordIds}
/>
</td>
<td>
<a href="#">{suggestion.title}</a>
</td>
<td>
<a href="#">
{suggestion.schemaName}
</a>
</td>
</tr>
{/each}
</tbody>
</table>
</form>
{:else}
<progress />
{/if}
</article>
</dialog>
<div>
{#if edgeRecordPreviews.length == 0}
No relations exist
{:else}
<Sortable
onUpdate={handleSortUpdate}
items={edgeRecordPreviews}
itemKey="edge.id"
>
{#snippet itemView(edgeRecordPreview)}
<div>
<a href="#">{edgeRecordPreview.recordPreview.title}</a>
{edgeRecordPreview.recordPreview.schemaName}
<button
onclick={(e) =>
handleRemoveEdge(edgeRecordPreview.edge.id)}
>remove</button
>
</div>
{/snippet}
</Sortable>
{/if}
</div>
</div>
@@ -1,7 +1,9 @@
<script>
import { get, post } from "../../../modules/remote";
import { getApp } from "../../../app";
import { getLocaleName } from "../locale.svelte.js";
import FieldLabel from "./FieldLabel.svelte";
import FieldError from "./FieldError.svelte";
import Sortable from "../../../common/Sortable.svelte";
let {
channel,
record,
@@ -16,7 +18,6 @@
let valuesChanged = $derived(newValue !== originalValue);
let errorMessage = $state("");
// let validationErrorState = $state(validationError);
let hasError = $derived(!!validationError);
const app = getApp();
let suggestionsLoaded = $state(false);
@@ -57,7 +58,41 @@
(data, err) => {
suggestionsLoaded = true;
dialog.close();
edgeRecordPreviews = data;
edgeRecordPreviews = data.edgeRecordPreviews;
validationError = data.validationError;
},
);
}
function handleSortUpdate(updatedEdges) {
// let updatedFieldIds = updatedFields.map((f) => f.id);
// fields = fields.filter((f) => !updatedFieldIds.includes(f.id));
// fields = [...fields, ...updatedFields];
post(
app.url("records/sort-edges"),
{
ids: updatedEdges.map((e) => e.edge.id),
},
(data, err) => {
edgeRecordPreviews = updatedEdges;
},
);
}
function handleRemoveEdge(edgeId) {
post(
app.url("edges/delete"),
{
id: edgeId,
from: record.id,
fieldId: schemaField.id,
locale: locale,
},
(data, err) => {
edgeRecordPreviews = edgeRecordPreviews.filter(
(e) => e.edge.id !== edgeId,
);
validationError = data.validationError;
},
);
}
@@ -65,17 +100,9 @@
<div style="min-width: 400px;">
<label>
{#if locale !== "main"}
{getLocaleName(channel, locale)} >
{/if}
{schemaField.name} <br />
<FieldLabel {locale} {channel} {schemaField}></FieldLabel>
</label>
{#if hasError}
<small id={schemaField.id + "-help"}>{validationError.message}</small>
{:else if schemaField.help != ""}
<small id={schemaField.id + "-help"}>{schemaField.help}</small>
{/if}
<FieldError {schemaField} {validationError}></FieldError>
<button onclick={handleModalOpen}>Choose record</button>
<dialog bind:this={dialog}>
@@ -125,12 +152,23 @@
{#if edgeRecordPreviews.length == 0}
No relations exist
{:else}
{#each edgeRecordPreviews as edgeRecordPreview}
<div>
<a href="#">{edgeRecordPreview.recordPreview.title}</a>
{edgeRecordPreview.recordPreview.schemaName}
</div>
{/each}
<Sortable
onUpdate={handleSortUpdate}
items={edgeRecordPreviews}
itemKey="edge.id"
>
{#snippet itemView(edgeRecordPreview)}
<div>
<a href="#">{edgeRecordPreview.recordPreview.title}</a>
{edgeRecordPreview.recordPreview.schemaName}
<button
onclick={(e) =>
handleRemoveEdge(edgeRecordPreview.edge.id)}
>remove</button
>
</div>
{/snippet}
</Sortable>
{/if}
</div>
</div>
@@ -1,15 +1,14 @@
<script>
import { post } from "../../../modules/remote";
import { getApp } from "../../../app";
import { getLocaleName } from "../locale.svelte.js";
import FieldLabel from "./FieldLabel.svelte";
import FieldError from "./FieldError.svelte";
let { channel, record, schemaField, dataField, locale, validationError } =
$props();
let originalValue = dataField?.value ?? schemaField.props.default;
let newValue = $state(originalValue);
let valuesChanged = $derived(newValue !== originalValue);
let errorMessage = $state("");
// let validationErrorState = $state(validationError);
let hasError = $derived(!!validationError);
const app = getApp();
function save() {
@@ -81,33 +80,9 @@
};
</script>
<!-- <div style="position: relative;">
{#if field.selectOptions}
<Autocomplete {field} bind:value></Autocomplete>
{:else}
<input
type="text"
{id}
class="form-control"
class:is-invalid={errorMessage}
bind:value
autocomplete="off"
readonly={field.readonly && !isCreateMode}
/>
{/if}
{#if errorMessage}
<div class="invalid-feedback d-block">
{errorMessage}
</div>
{/if}
</div> -->
<div style="min-width: 400px;">
<label>
{#if locale !== "main"}
{getLocaleName(channel, locale)} >
{/if}
{schemaField.name} <br />
<FieldLabel {locale} {channel} {schemaField}></FieldLabel>
<input
type="text"
@@ -120,14 +95,8 @@
onblur={handleBlur}
oncompositionstart={handleCompositionStart}
oncompositionend={handleCompositionEnd}
aria-invalid={hasError ? "true" : ""}
/>
{#if hasError}
<small id={schemaField.id + "-help"}
>{validationError.message}</small
>
{:else if schemaField.help != ""}
<small id={schemaField.id + "-help"}>{schemaField.help}</small>
{/if}
<!-- aria-invalid={hasError ? "true" : ""} -->
<FieldError {schemaField} {validationError}></FieldError>
</label>
</div>