@lobb-js/studio 0.29.0 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/actions.d.ts +11 -0
- package/dist/actions.js +16 -0
- package/dist/components/Studio.svelte +39 -43
- package/dist/components/StudioRoot.svelte +19 -0
- package/dist/components/StudioRoot.svelte.d.ts +6 -0
- package/dist/components/breadCrumbs.svelte +5 -4
- package/dist/components/codeEditor.svelte +1 -1
- package/dist/components/dataTable/dataTable.svelte +35 -20
- package/dist/components/dataTable/dataTable.svelte.d.ts +2 -1
- package/dist/components/dataTable/dataTableTabs.svelte +4 -2
- package/dist/components/dataTable/dataTableTabs.svelte.d.ts +2 -0
- package/dist/components/dataTable/header.svelte +15 -11
- package/dist/components/dataTable/header.svelte.d.ts +1 -0
- package/dist/components/dataTable/listViewChildren.svelte +4 -6
- package/dist/components/dataTable/listViewChildren.svelte.d.ts +0 -1
- package/dist/components/dataTable/table.svelte +8 -10
- package/dist/components/dataTable/table.svelte.d.ts +0 -1
- package/dist/components/dataTableDrawer/dataTableDrawer.svelte +4 -1
- package/dist/components/dataTableDrawer/dataTableDrawer.svelte.d.ts +2 -0
- package/dist/components/dataTablePopup/dataTablePopup.svelte +67 -0
- package/dist/components/dataTablePopup/dataTablePopup.svelte.d.ts +15 -0
- package/dist/components/detailView/create/children.svelte +1 -1
- package/dist/components/detailView/create/createDetailView.svelte +19 -61
- package/dist/components/detailView/create/createManyView.svelte +2 -4
- package/dist/components/detailView/detailView.svelte +81 -0
- package/dist/components/detailView/detailView.svelte.d.ts +8 -0
- package/dist/components/detailView/fieldInput.svelte +10 -10
- package/dist/components/detailView/fieldInputReplacement.svelte +7 -7
- package/dist/components/detailView/passwordInput.svelte +1 -1
- package/dist/components/detailView/update/updateDetailView.svelte +32 -69
- package/dist/components/diffViewer.svelte +1 -1
- package/dist/components/extensionsComponents.svelte +3 -1
- package/dist/components/foreingKeyInput.svelte +2 -2
- package/dist/components/importButton.svelte +12 -9
- package/dist/components/landing.svelte +7 -0
- package/dist/components/landing.svelte.d.ts +6 -14
- package/dist/components/miniSidebar.svelte +86 -15
- package/dist/components/miniSidebar.svelte.d.ts +2 -17
- package/dist/components/polymorphicInput.svelte +1 -1
- package/dist/components/rangeCalendarButton.svelte +10 -10
- package/dist/components/richTextEditor.svelte +1 -1
- package/dist/components/routes/collections/collections.svelte +32 -10
- package/dist/components/routes/data_model/dataModel.svelte +6 -28
- package/dist/components/routes/data_model/dataModel.svelte.d.ts +17 -2
- package/dist/components/routes/extensions/publicExtension.svelte +19 -0
- package/dist/components/routes/extensions/publicExtension.svelte.d.ts +13 -0
- package/dist/components/routes/home.svelte +2 -2
- package/dist/components/routes/workflows/workflows.svelte +4 -4
- package/dist/components/sidebar/sidebar.svelte +1 -1
- package/dist/components/sidebar/sidebarElements.svelte +4 -4
- package/dist/components/singletone.svelte +4 -6
- package/dist/components/ui/button/button.svelte +2 -3
- package/dist/components/workflowEditor.svelte +2 -2
- package/dist/eventSystem.d.ts +1 -1
- package/dist/eventSystem.js +7 -5
- package/dist/extensions/extension.types.d.ts +39 -14
- package/dist/extensions/extensionUtils.js +6 -3
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/store.types.d.ts +1 -1
- package/dist/studioLifecycle.svelte.d.ts +2 -0
- package/dist/studioLifecycle.svelte.js +15 -0
- package/package.json +3 -4
- package/src/app.css +3 -0
- package/src/lib/actions.ts +28 -0
- package/src/lib/components/Studio.svelte +39 -43
- package/src/lib/components/StudioRoot.svelte +19 -0
- package/src/lib/components/breadCrumbs.svelte +5 -4
- package/src/lib/components/codeEditor.svelte +1 -1
- package/src/lib/components/dataTable/dataTable.svelte +35 -20
- package/src/lib/components/dataTable/dataTableTabs.svelte +4 -2
- package/src/lib/components/dataTable/header.svelte +15 -11
- package/src/lib/components/dataTable/listViewChildren.svelte +4 -6
- package/src/lib/components/dataTable/table.svelte +8 -10
- package/src/lib/components/dataTableDrawer/dataTableDrawer.svelte +4 -1
- package/src/lib/components/dataTablePopup/dataTablePopup.svelte +67 -0
- package/src/lib/components/detailView/create/children.svelte +1 -1
- package/src/lib/components/detailView/create/createDetailView.svelte +19 -61
- package/src/lib/components/detailView/create/createManyView.svelte +2 -4
- package/src/lib/components/detailView/detailView.svelte +81 -0
- package/src/lib/components/detailView/fieldInput.svelte +10 -10
- package/src/lib/components/detailView/fieldInputReplacement.svelte +7 -7
- package/src/lib/components/detailView/passwordInput.svelte +1 -1
- package/src/lib/components/detailView/update/updateDetailView.svelte +32 -69
- package/src/lib/components/diffViewer.svelte +1 -1
- package/src/lib/components/extensionsComponents.svelte +3 -1
- package/src/lib/components/foreingKeyInput.svelte +2 -2
- package/src/lib/components/importButton.svelte +12 -9
- package/src/lib/components/landing.svelte +7 -0
- package/src/lib/components/miniSidebar.svelte +86 -15
- package/src/lib/components/polymorphicInput.svelte +1 -1
- package/src/lib/components/rangeCalendarButton.svelte +10 -10
- package/src/lib/components/richTextEditor.svelte +1 -1
- package/src/lib/components/routes/collections/collections.svelte +32 -10
- package/src/lib/components/routes/data_model/dataModel.svelte +6 -28
- package/src/lib/components/routes/extensions/publicExtension.svelte +19 -0
- package/src/lib/components/routes/home.svelte +2 -2
- package/src/lib/components/routes/workflows/workflows.svelte +4 -4
- package/src/lib/components/sidebar/sidebar.svelte +1 -1
- package/src/lib/components/sidebar/sidebarElements.svelte +4 -4
- package/src/lib/components/singletone.svelte +4 -6
- package/src/lib/components/ui/button/button.svelte +2 -3
- package/src/lib/components/workflowEditor.svelte +2 -2
- package/src/lib/eventSystem.ts +8 -7
- package/src/lib/extensions/extension.types.ts +40 -6
- package/src/lib/extensions/extensionUtils.ts +6 -3
- package/src/lib/index.ts +3 -1
- package/src/lib/store.types.ts +1 -1
- package/src/lib/studioLifecycle.svelte.ts +17 -0
- package/dist/components/routes/data_model/syncManager.svelte +0 -94
- package/dist/components/routes/data_model/syncManager.svelte.d.ts +0 -3
- package/src/lib/components/routes/data_model/syncManager.svelte +0 -94
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
parentCollectionName={collectionName}
|
|
38
38
|
collectionName={child.collection}
|
|
39
39
|
parentRecord={{ id: entry.id, collectionName }}
|
|
40
|
-
class="bg-muted
|
|
40
|
+
class="bg-muted-soft border rounded-md overflow-hidden"
|
|
41
41
|
bind:value={entry[child.collection]}
|
|
42
42
|
>
|
|
43
43
|
<CreateManyView
|
|
@@ -24,53 +24,46 @@
|
|
|
24
24
|
import { untrack } from "svelte";
|
|
25
25
|
|
|
26
26
|
const { lobb, ctx } = getStudioContext();
|
|
27
|
-
import ExtensionsComponents from "../../extensionsComponents.svelte";
|
|
28
|
-
import { getExtensionUtils } from "../../../extensions/extensionUtils";
|
|
29
|
-
import { getField, getFieldIcon } from "../../dataTable/utils";
|
|
30
27
|
import Children from "./children.svelte";
|
|
31
28
|
import { buildChildren, getDefaultEntry } from "../utils";
|
|
32
29
|
import type { Changes } from "../utils";
|
|
33
30
|
import { getChangedProperties } from "../../../utils";
|
|
34
31
|
import type { Snippet } from "svelte";
|
|
35
|
-
import
|
|
32
|
+
import DetailView from "../detailView.svelte";
|
|
36
33
|
import Drawer from "../../drawer.svelte";
|
|
37
34
|
|
|
38
35
|
let {
|
|
39
36
|
collectionName,
|
|
40
|
-
values = {},
|
|
37
|
+
values: passedValues = {} as Record<string, any>,
|
|
41
38
|
showRelatedRecords = true,
|
|
42
39
|
onCancel,
|
|
43
40
|
onSuccessfullSave,
|
|
44
41
|
title,
|
|
45
42
|
submitButton,
|
|
46
|
-
changes = $bindable<Changes | undefined>(undefined),
|
|
43
|
+
changes: passedChanges = $bindable<Changes | undefined>(undefined),
|
|
47
44
|
}: CreateDetailViewProp = $props();
|
|
48
45
|
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
const isRecordingMode = passedChanges !== undefined;
|
|
47
|
+
if (!isRecordingMode) passedChanges = { data: {}, children: {} };
|
|
48
|
+
const changes = passedChanges as Changes;
|
|
51
49
|
|
|
52
50
|
const fieldNames = Object.keys(ctx.meta.collections[collectionName].fields);
|
|
53
|
-
let
|
|
54
|
-
getDefaultEntry(ctx, fieldNames, collectionName, values),
|
|
55
|
-
);
|
|
56
|
-
const initialEntry = $state.snapshot(entry);
|
|
51
|
+
let values = $state(getDefaultEntry(ctx, fieldNames, collectionName, passedValues));
|
|
57
52
|
let fieldsErrors: Record<string, any> = $state({});
|
|
58
53
|
|
|
59
54
|
const childCollections = ctx.meta.relations
|
|
60
55
|
.filter((r) => r.to.collection === collectionName)
|
|
61
56
|
.map((r) => (r as any).from.collection);
|
|
62
57
|
|
|
63
|
-
const subCollections = childCollections;
|
|
64
58
|
const subCollectionsValues: Record<string, any> = {};
|
|
65
|
-
for (const col of
|
|
66
|
-
if (
|
|
59
|
+
for (const col of childCollections) {
|
|
60
|
+
if (passedValues[col]) subCollectionsValues[col] = passedValues[col];
|
|
67
61
|
}
|
|
68
62
|
|
|
69
63
|
$effect(() => {
|
|
70
|
-
const snap = $state.snapshot(
|
|
64
|
+
const snap = $state.snapshot(values);
|
|
71
65
|
|
|
72
66
|
untrack(() => {
|
|
73
|
-
const target = changes ?? _changes;
|
|
74
67
|
const data: Record<string, any> = {};
|
|
75
68
|
const children: Record<string, any> = {};
|
|
76
69
|
|
|
@@ -88,17 +81,17 @@
|
|
|
88
81
|
}
|
|
89
82
|
}
|
|
90
83
|
|
|
91
|
-
|
|
92
|
-
|
|
84
|
+
changes.data = data;
|
|
85
|
+
changes.children = children;
|
|
93
86
|
|
|
94
87
|
if (!isRecordingMode) {
|
|
95
|
-
console.log(`[${collectionName}] changes:`, $state.snapshot(
|
|
88
|
+
console.log(`[${collectionName}] changes:`, $state.snapshot(changes));
|
|
96
89
|
}
|
|
97
90
|
});
|
|
98
91
|
});
|
|
99
92
|
|
|
100
93
|
function handleCancel() {
|
|
101
|
-
if (
|
|
94
|
+
if (isRecordingMode) {
|
|
102
95
|
changes.data = {};
|
|
103
96
|
changes.children = {};
|
|
104
97
|
}
|
|
@@ -106,14 +99,13 @@
|
|
|
106
99
|
}
|
|
107
100
|
|
|
108
101
|
async function handleSave() {
|
|
109
|
-
const
|
|
110
|
-
const snap = $state.snapshot(target);
|
|
102
|
+
const snap = $state.snapshot(changes);
|
|
111
103
|
|
|
112
104
|
const children = buildChildren(ctx, collectionName, { ...snap.data, ...Object.fromEntries(
|
|
113
105
|
Object.entries(snap.children).map(([col, ops]) => [
|
|
114
106
|
col,
|
|
115
107
|
[
|
|
116
|
-
...(ops.created.map((
|
|
108
|
+
...(ops.created.map((op) => op.data)),
|
|
117
109
|
...(ops.linked.map((id) => ({ id }))),
|
|
118
110
|
]
|
|
119
111
|
])
|
|
@@ -135,6 +127,7 @@
|
|
|
135
127
|
fieldsErrors = result.details;
|
|
136
128
|
return;
|
|
137
129
|
} else if (result.message) {
|
|
130
|
+
toast.error(result.message);
|
|
138
131
|
return;
|
|
139
132
|
}
|
|
140
133
|
}
|
|
@@ -166,44 +159,9 @@
|
|
|
166
159
|
</div>
|
|
167
160
|
</div>
|
|
168
161
|
<div class="flex-1 overflow-y-auto">
|
|
169
|
-
<
|
|
170
|
-
{#each fieldNames as fieldName}
|
|
171
|
-
{#if !ctx.meta.collections[collectionName].fields[fieldName]?.ui?.hidden}
|
|
172
|
-
{@const field = getField(ctx, fieldName, collectionName)}
|
|
173
|
-
{@const FieldIcon = getFieldIcon(ctx, fieldName, collectionName)}
|
|
174
|
-
<div class="flex flex-col gap-2">
|
|
175
|
-
<div class="flex flex-1 items-end justify-between gap-2 text-xs">
|
|
176
|
-
<div class="flex gap-2">
|
|
177
|
-
<div class="h-fit">{field.label}</div>
|
|
178
|
-
<div class="flex h-fit items-center gap-1 text-[0.7rem] text-muted-foreground">
|
|
179
|
-
<FieldIcon size="12" />
|
|
180
|
-
{field.type}
|
|
181
|
-
</div>
|
|
182
|
-
</div>
|
|
183
|
-
<div>
|
|
184
|
-
<ExtensionsComponents
|
|
185
|
-
name="dvFields.topRight.{collectionName}.{fieldName}"
|
|
186
|
-
utils={getExtensionUtils(lobb, ctx)}
|
|
187
|
-
bind:value={entry[fieldName]}
|
|
188
|
-
/>
|
|
189
|
-
</div>
|
|
190
|
-
</div>
|
|
191
|
-
<FieldInput
|
|
192
|
-
{collectionName}
|
|
193
|
-
{fieldName}
|
|
194
|
-
bind:value={
|
|
195
|
-
() => entry[fieldName],
|
|
196
|
-
(v) => (entry = { ...entry, [fieldName]: v })
|
|
197
|
-
}
|
|
198
|
-
bind:entry
|
|
199
|
-
errorMessages={fieldsErrors[fieldName]}
|
|
200
|
-
/>
|
|
201
|
-
</div>
|
|
202
|
-
{/if}
|
|
203
|
-
{/each}
|
|
204
|
-
</div>
|
|
162
|
+
<DetailView {collectionName} bind:entry={values} {fieldsErrors} />
|
|
205
163
|
{#if showRelatedRecords}
|
|
206
|
-
<Children {collectionName} values={subCollectionsValues} bind:entry />
|
|
164
|
+
<Children {collectionName} values={subCollectionsValues} bind:entry={values} />
|
|
207
165
|
{/if}
|
|
208
166
|
</div>
|
|
209
167
|
<div class="flex h-12 items-center justify-end gap-2 border-t px-4">
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
>
|
|
113
113
|
<div
|
|
114
114
|
class="
|
|
115
|
-
flex items-center justify-between px-2 h-10 bg-muted
|
|
115
|
+
flex items-center justify-between px-2 h-10 bg-muted-soft
|
|
116
116
|
{expanded ? 'border-b' : ''}
|
|
117
117
|
"
|
|
118
118
|
>
|
|
@@ -165,13 +165,12 @@
|
|
|
165
165
|
</div>
|
|
166
166
|
</div>
|
|
167
167
|
{#if expanded}
|
|
168
|
-
<div bind:clientWidth={tableWidth} class="bg-muted
|
|
168
|
+
<div bind:clientWidth={tableWidth} class="bg-muted-soft overflow-auto">
|
|
169
169
|
<Table
|
|
170
170
|
data={entries}
|
|
171
171
|
{columns}
|
|
172
172
|
selectByColumn="id"
|
|
173
173
|
showCollapsible={doesCollectionHasChildren}
|
|
174
|
-
unifiedBgColor="bg-muted/30"
|
|
175
174
|
>
|
|
176
175
|
{#snippet tools(entry, index)}
|
|
177
176
|
<Button
|
|
@@ -240,7 +239,6 @@
|
|
|
240
239
|
{collectionName}
|
|
241
240
|
recordId={entry.id}
|
|
242
241
|
width={tableWidth}
|
|
243
|
-
unifiedBgColor="bg-muted/30"
|
|
244
242
|
/>
|
|
245
243
|
{:else}
|
|
246
244
|
<SubRecords
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { CircleHelp } from "lucide-svelte";
|
|
3
|
+
import * as Tooltip from "../ui/tooltip";
|
|
4
|
+
import { getStudioContext } from "../../context";
|
|
5
|
+
import ExtensionsComponents from "../extensionsComponents.svelte";
|
|
6
|
+
import { getExtensionUtils } from "../../extensions/extensionUtils";
|
|
7
|
+
import { getField, getFieldIcon } from "../dataTable/utils";
|
|
8
|
+
import FieldInput from "./fieldInput.svelte";
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
collectionName: string;
|
|
12
|
+
entry: Record<string, any>;
|
|
13
|
+
fieldsErrors?: Record<string, string[]>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let {
|
|
17
|
+
collectionName,
|
|
18
|
+
entry = $bindable(),
|
|
19
|
+
fieldsErrors = {},
|
|
20
|
+
}: Props = $props();
|
|
21
|
+
|
|
22
|
+
const { lobb, ctx } = getStudioContext();
|
|
23
|
+
const fieldNames = $derived(
|
|
24
|
+
Object.keys(ctx.meta.collections[collectionName].fields),
|
|
25
|
+
);
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<div class="flex flex-col gap-4 p-4">
|
|
29
|
+
{#each fieldNames as fieldName}
|
|
30
|
+
{#if !ctx.meta.collections[collectionName].fields[fieldName]?.ui?.hidden}
|
|
31
|
+
{@const field = getField(ctx, fieldName, collectionName)}
|
|
32
|
+
{@const FieldIcon = getFieldIcon(ctx, fieldName, collectionName)}
|
|
33
|
+
{@const description = ctx.meta.collections[collectionName].fields[fieldName]?.description}
|
|
34
|
+
<div class="flex flex-col gap-2">
|
|
35
|
+
<div class="flex flex-1 items-end justify-between gap-2 text-xs">
|
|
36
|
+
<div class="flex items-center gap-1.5">
|
|
37
|
+
<ExtensionsComponents
|
|
38
|
+
name="detailView.field.label"
|
|
39
|
+
utils={getExtensionUtils(lobb, ctx)}
|
|
40
|
+
{collectionName}
|
|
41
|
+
{fieldName}
|
|
42
|
+
bind:value={entry[fieldName]}
|
|
43
|
+
>
|
|
44
|
+
<div class="flex items-center gap-1.5">
|
|
45
|
+
<div class="h-fit">{field.label}</div>
|
|
46
|
+
<div class="flex h-fit items-center gap-1 text-[0.7rem] text-muted-foreground">
|
|
47
|
+
<FieldIcon size="12" />
|
|
48
|
+
{field.type}
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
</ExtensionsComponents>
|
|
52
|
+
{#if description}
|
|
53
|
+
<Tooltip.Root>
|
|
54
|
+
<Tooltip.Trigger>
|
|
55
|
+
<CircleHelp size="12" class="text-muted-foreground" />
|
|
56
|
+
</Tooltip.Trigger>
|
|
57
|
+
<Tooltip.Content class="max-w-64 text-xs">
|
|
58
|
+
{description}
|
|
59
|
+
</Tooltip.Content>
|
|
60
|
+
</Tooltip.Root>
|
|
61
|
+
{/if}
|
|
62
|
+
</div>
|
|
63
|
+
<div>
|
|
64
|
+
<ExtensionsComponents
|
|
65
|
+
name="dvFields.topRight.{collectionName}.{fieldName}"
|
|
66
|
+
utils={getExtensionUtils(lobb, ctx)}
|
|
67
|
+
bind:value={entry[fieldName]}
|
|
68
|
+
/>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
<FieldInput
|
|
72
|
+
{collectionName}
|
|
73
|
+
{fieldName}
|
|
74
|
+
bind:value={entry[fieldName]}
|
|
75
|
+
bind:entry
|
|
76
|
+
errorMessages={fieldsErrors[fieldName]}
|
|
77
|
+
/>
|
|
78
|
+
</div>
|
|
79
|
+
{/if}
|
|
80
|
+
{/each}
|
|
81
|
+
</div>
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
{:else if field.label === "id"}
|
|
87
87
|
<Input
|
|
88
88
|
placeholder="AUTO GENERATED"
|
|
89
|
-
class="bg-muted
|
|
89
|
+
class="bg-muted-soft text-xs"
|
|
90
90
|
bind:value
|
|
91
91
|
/>
|
|
92
92
|
{:else if fieldRelationTarget && entry}
|
|
@@ -126,7 +126,7 @@
|
|
|
126
126
|
>
|
|
127
127
|
<Select.Trigger
|
|
128
128
|
class="
|
|
129
|
-
h-9 w-full bg-muted
|
|
129
|
+
h-9 w-full bg-muted-soft pr-8
|
|
130
130
|
{destructive ? 'border-destructive bg-destructive/10' : ''}
|
|
131
131
|
"
|
|
132
132
|
>
|
|
@@ -158,7 +158,7 @@
|
|
|
158
158
|
placeholder={ui?.placeholder ? ui.placeholder : "NULL"}
|
|
159
159
|
type="text"
|
|
160
160
|
class="
|
|
161
|
-
bg-muted
|
|
161
|
+
bg-muted-soft text-xs
|
|
162
162
|
{destructive ? 'border-destructive bg-destructive/10' : ''}
|
|
163
163
|
"
|
|
164
164
|
bind:value
|
|
@@ -168,7 +168,7 @@
|
|
|
168
168
|
placeholder={ui?.placeholder ? ui.placeholder : value === "" ? "EMPTY STRING" : "NULL"}
|
|
169
169
|
rows={5}
|
|
170
170
|
class="
|
|
171
|
-
bg-muted
|
|
171
|
+
bg-muted-soft text-xs
|
|
172
172
|
{destructive ? 'border-destructive bg-destructive/10' : ''}
|
|
173
173
|
"
|
|
174
174
|
bind:value
|
|
@@ -178,7 +178,7 @@
|
|
|
178
178
|
type="date"
|
|
179
179
|
placeholder={ui?.placeholder ? ui.placeholder : "NULL"}
|
|
180
180
|
class="
|
|
181
|
-
dateInput block w-full bg-muted
|
|
181
|
+
dateInput block w-full bg-muted-soft pr-9 text-xs
|
|
182
182
|
{destructive ? 'border-destructive bg-destructive/10' : ''}
|
|
183
183
|
"
|
|
184
184
|
bind:value={
|
|
@@ -197,7 +197,7 @@
|
|
|
197
197
|
type="time"
|
|
198
198
|
placeholder={ui?.placeholder ? ui.placeholder : "NULL"}
|
|
199
199
|
class="
|
|
200
|
-
dateInput block w-full bg-muted
|
|
200
|
+
dateInput block w-full bg-muted-soft pr-9 text-xs
|
|
201
201
|
{destructive ? 'border-destructive bg-destructive/10' : ''}
|
|
202
202
|
"
|
|
203
203
|
bind:value={
|
|
@@ -215,7 +215,7 @@
|
|
|
215
215
|
type="datetime-local"
|
|
216
216
|
placeholder={ui?.placeholder ? ui.placeholder : "NULL"}
|
|
217
217
|
class="
|
|
218
|
-
dateInput block w-full bg-muted
|
|
218
|
+
dateInput block w-full bg-muted-soft pr-9 text-xs
|
|
219
219
|
{destructive ? 'border-destructive bg-destructive/10' : ''}
|
|
220
220
|
"
|
|
221
221
|
bind:value={
|
|
@@ -236,7 +236,7 @@
|
|
|
236
236
|
<Select.Trigger
|
|
237
237
|
placeholder={ui?.placeholder ? ui.placeholder : "NULL"}
|
|
238
238
|
class="
|
|
239
|
-
bg-muted
|
|
239
|
+
bg-muted-soft pr-9
|
|
240
240
|
{destructive ? 'border-destructive bg-destructive/10' : ''}
|
|
241
241
|
"
|
|
242
242
|
>
|
|
@@ -263,7 +263,7 @@
|
|
|
263
263
|
type="number"
|
|
264
264
|
step="any"
|
|
265
265
|
class="
|
|
266
|
-
bg-muted
|
|
266
|
+
bg-muted-soft text-xs
|
|
267
267
|
{destructive ? 'border-destructive bg-destructive/10' : ''}
|
|
268
268
|
"
|
|
269
269
|
bind:value
|
|
@@ -273,7 +273,7 @@
|
|
|
273
273
|
placeholder={ui?.placeholder ? ui.placeholder : "NULL"}
|
|
274
274
|
type="number"
|
|
275
275
|
class="
|
|
276
|
-
bg-muted
|
|
276
|
+
bg-muted-soft text-xs
|
|
277
277
|
{destructive ? 'border-destructive bg-destructive/10' : ''}
|
|
278
278
|
"
|
|
279
279
|
bind:value
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
placeholder={field.placeholder ? field.placeholder : "NULL"}
|
|
50
50
|
type="text"
|
|
51
51
|
class="
|
|
52
|
-
bg-muted
|
|
52
|
+
bg-muted-soft text-xs
|
|
53
53
|
{destructive ? 'border-destructive bg-destructive/10' : ''}
|
|
54
54
|
"
|
|
55
55
|
bind:value
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
<Select.Trigger
|
|
65
65
|
placeholder={field.placeholder ? field.placeholder : "NULL"}
|
|
66
66
|
class="
|
|
67
|
-
h-9 w-full bg-muted
|
|
67
|
+
h-9 w-full bg-muted-soft pr-8
|
|
68
68
|
{destructive ? 'border-destructive bg-destructive/10' : ''}
|
|
69
69
|
"
|
|
70
70
|
>
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
placeholder={field.placeholder ? field.placeholder : value === "" ? "EMPTY STRING" : "NULL"}
|
|
85
85
|
rows={5}
|
|
86
86
|
class="
|
|
87
|
-
bg-muted
|
|
87
|
+
bg-muted-soft text-xs
|
|
88
88
|
{destructive ? 'border-destructive bg-destructive/10' : ''}
|
|
89
89
|
"
|
|
90
90
|
bind:value
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
<Input
|
|
99
99
|
type="date"
|
|
100
100
|
class="
|
|
101
|
-
dateInput block w-full bg-muted
|
|
101
|
+
dateInput block w-full bg-muted-soft pr-9 text-xs
|
|
102
102
|
{destructive ? 'border-destructive bg-destructive/10' : ''}
|
|
103
103
|
"
|
|
104
104
|
bind:value={
|
|
@@ -116,7 +116,7 @@
|
|
|
116
116
|
<Input
|
|
117
117
|
type="time"
|
|
118
118
|
class="
|
|
119
|
-
dateInput block w-full bg-muted
|
|
119
|
+
dateInput block w-full bg-muted-soft pr-9 text-xs
|
|
120
120
|
{destructive ? 'border-destructive bg-destructive/10' : ''}
|
|
121
121
|
"
|
|
122
122
|
bind:value={
|
|
@@ -133,7 +133,7 @@
|
|
|
133
133
|
<Input
|
|
134
134
|
type="datetime-local"
|
|
135
135
|
class="
|
|
136
|
-
dateInput block w-full bg-muted
|
|
136
|
+
dateInput block w-full bg-muted-soft pr-9 text-xs
|
|
137
137
|
{destructive ? 'border-destructive bg-destructive/10' : ''}
|
|
138
138
|
"
|
|
139
139
|
bind:value={
|
|
@@ -154,7 +154,7 @@
|
|
|
154
154
|
<Select.Root type="single" bind:value>
|
|
155
155
|
<Select.Trigger
|
|
156
156
|
class="
|
|
157
|
-
bg-muted
|
|
157
|
+
bg-muted-soft pr-9
|
|
158
158
|
{destructive ? 'border-destructive bg-destructive/10' : ''}
|
|
159
159
|
"
|
|
160
160
|
>
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
<Input
|
|
22
22
|
type="password"
|
|
23
23
|
placeholder="••••••"
|
|
24
|
-
class="bg-muted
|
|
24
|
+
class="bg-muted-soft text-xs {destructive ? 'border-destructive bg-destructive/10' : ''}"
|
|
25
25
|
value={displayValue}
|
|
26
26
|
oninput={onInput}
|
|
27
27
|
/>
|
|
@@ -22,67 +22,70 @@
|
|
|
22
22
|
import Button from "../../ui/button/button.svelte";
|
|
23
23
|
import { getStudioContext } from "../../../context";
|
|
24
24
|
import { toast } from "svelte-sonner";
|
|
25
|
-
import ExtensionsComponents from "../../extensionsComponents.svelte";
|
|
26
|
-
import { getExtensionUtils } from "../../../extensions/extensionUtils";
|
|
27
25
|
import { untrack } from "svelte";
|
|
28
26
|
|
|
29
27
|
const { lobb, ctx } = getStudioContext();
|
|
30
28
|
import { getChangedProperties } from "../../../utils";
|
|
31
|
-
import { getField, getFieldIcon } from "../../dataTable/utils";
|
|
32
29
|
import DetailViewChildren from "./detailViewChildren.svelte";
|
|
33
30
|
import type { Snippet } from "svelte";
|
|
34
31
|
import { getDefaultEntry } from "../utils";
|
|
35
32
|
import type { Changes, ChildrenChanges } from "../utils";
|
|
36
|
-
import
|
|
33
|
+
import DetailView from "../detailView.svelte";
|
|
37
34
|
import Drawer from "../../drawer.svelte";
|
|
38
35
|
|
|
39
36
|
let {
|
|
40
37
|
collectionName,
|
|
41
|
-
values = {},
|
|
38
|
+
values: passedValues = {} as Record<string, any>,
|
|
42
39
|
showRelatedRecords = true,
|
|
43
40
|
onCancel,
|
|
44
41
|
onSuccessfullSave,
|
|
45
42
|
title,
|
|
46
43
|
submitButton,
|
|
47
44
|
recordId,
|
|
48
|
-
changes = $bindable<Changes | undefined>(undefined),
|
|
45
|
+
changes: passedChanges = $bindable<Changes | undefined>(undefined),
|
|
49
46
|
}: UpdateDetailViewProp = $props();
|
|
50
47
|
|
|
51
|
-
// Internal changes — used when not in recording mode, passed down to children
|
|
52
|
-
let _changes = $state<Changes>({ data: {}, children: {} });
|
|
53
|
-
|
|
54
48
|
// Recording mode = changes was passed from a parent component
|
|
55
|
-
const isRecordingMode =
|
|
49
|
+
const isRecordingMode = passedChanges !== undefined;
|
|
50
|
+
if (!isRecordingMode) passedChanges = { data: {}, children: {} };
|
|
51
|
+
const changes = passedChanges as Changes;
|
|
56
52
|
|
|
57
53
|
const fieldNames = Object.keys(ctx.meta.collections[collectionName].fields);
|
|
58
|
-
let
|
|
59
|
-
|
|
60
|
-
);
|
|
61
|
-
const initialEntry = $state.snapshot(entry);
|
|
54
|
+
let values = $state(getDefaultEntry(ctx, fieldNames, collectionName, passedValues));
|
|
55
|
+
const initialValues = $state.snapshot(values);
|
|
62
56
|
let fieldsErrors: Record<string, any> = $state({});
|
|
63
57
|
|
|
64
|
-
|
|
58
|
+
const hasChanges = $derived(
|
|
59
|
+
Object.keys(changes.data).length > 0 ||
|
|
60
|
+
Object.values(changes.children).some(
|
|
61
|
+
(ch: ChildrenChanges) => ch.created.length || ch.updated.length || ch.deleted.length || ch.linked.length || ch.unlinked.length,
|
|
62
|
+
),
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
// Tracks top-level field edits into changes.data.
|
|
65
66
|
// Child ops (create/link/unlink/delete) are written directly by DataTable into changes.children.
|
|
66
67
|
$effect(() => {
|
|
67
|
-
const currentEntrySnap = $state.snapshot(
|
|
68
|
+
const currentEntrySnap = $state.snapshot(values);
|
|
68
69
|
|
|
69
70
|
untrack(() => {
|
|
70
|
-
|
|
71
|
-
target.data = getChangedProperties(initialEntry, currentEntrySnap);
|
|
72
|
-
|
|
73
|
-
if (!isRecordingMode) {
|
|
74
|
-
console.log(`[${collectionName}] changes:`, $state.snapshot(target));
|
|
75
|
-
}
|
|
71
|
+
changes.data = getChangedProperties(initialValues, currentEntrySnap);
|
|
76
72
|
});
|
|
77
73
|
});
|
|
78
74
|
|
|
75
|
+
// Separate logging effect — needs its own $effect so it tracks mutations to changes.children.
|
|
76
|
+
$effect(() => {
|
|
77
|
+
if (!isRecordingMode) {
|
|
78
|
+
console.log(`[${collectionName}] changes:`, $state.snapshot(changes));
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
79
82
|
function buildApiChildren(children: Record<string, ChildrenChanges>): Record<string, any> | undefined {
|
|
80
83
|
const result: Record<string, any> = {};
|
|
81
84
|
for (const [collection, ops] of Object.entries(children)) {
|
|
82
85
|
const hasOps = ops.created.length || ops.deleted.length || ops.linked.length || ops.unlinked.length;
|
|
83
86
|
if (!hasOps) continue;
|
|
84
87
|
result[collection] = {
|
|
85
|
-
...(ops.created.length ? { create: ops.created.map((
|
|
88
|
+
...(ops.created.length ? { create: ops.created.map((op) => op.data) } : {}),
|
|
86
89
|
...(ops.deleted.length ? { delete: ops.deleted.map((r) => r.id) } : {}),
|
|
87
90
|
...(ops.linked.length ? { link: ops.linked.map((r) => r.id) } : {}),
|
|
88
91
|
...(ops.unlinked.length ? { unlink: ops.unlinked.map((r) => r.id) } : {}),
|
|
@@ -92,7 +95,7 @@
|
|
|
92
95
|
}
|
|
93
96
|
|
|
94
97
|
function handleCancel() {
|
|
95
|
-
if (
|
|
98
|
+
if (isRecordingMode) {
|
|
96
99
|
changes.data = {};
|
|
97
100
|
changes.children = {};
|
|
98
101
|
}
|
|
@@ -100,8 +103,7 @@
|
|
|
100
103
|
}
|
|
101
104
|
|
|
102
105
|
async function handleSave() {
|
|
103
|
-
const
|
|
104
|
-
const snap = $state.snapshot(target);
|
|
106
|
+
const snap = $state.snapshot(changes);
|
|
105
107
|
const { id: _id, ...data } = snap.data;
|
|
106
108
|
const children = buildApiChildren(snap.children);
|
|
107
109
|
|
|
@@ -121,6 +123,7 @@
|
|
|
121
123
|
fieldsErrors = result.details;
|
|
122
124
|
return;
|
|
123
125
|
} else if (result.message) {
|
|
126
|
+
toast.error(result.message);
|
|
124
127
|
return;
|
|
125
128
|
}
|
|
126
129
|
}
|
|
@@ -128,7 +131,7 @@
|
|
|
128
131
|
|
|
129
132
|
// Real mode: also fire separate update requests for edited children
|
|
130
133
|
if (!isRecordingMode) {
|
|
131
|
-
for (const [collection, ops] of Object.entries(snap.children)) {
|
|
134
|
+
for (const [collection, ops] of Object.entries(snap.children) as [string, ChildrenChanges][]) {
|
|
132
135
|
for (const updated of ops.updated) {
|
|
133
136
|
await lobb.updateOne(collection, String(updated.id), updated.data);
|
|
134
137
|
}
|
|
@@ -139,14 +142,6 @@
|
|
|
139
142
|
toast.success(`The record was successfully updated`);
|
|
140
143
|
onCancel?.();
|
|
141
144
|
}
|
|
142
|
-
|
|
143
|
-
const activeChanges = $derived(changes ?? _changes);
|
|
144
|
-
const hasChanges = $derived(
|
|
145
|
-
Object.keys(activeChanges.data).length > 0 ||
|
|
146
|
-
Object.values(activeChanges.children).some(
|
|
147
|
-
(c) => c.created.length || c.updated.length || c.deleted.length || c.linked.length || c.unlinked.length,
|
|
148
|
-
),
|
|
149
|
-
);
|
|
150
145
|
</script>
|
|
151
146
|
|
|
152
147
|
<Drawer onHide={handleCancel}>
|
|
@@ -169,41 +164,9 @@
|
|
|
169
164
|
</div>
|
|
170
165
|
</div>
|
|
171
166
|
<div class="flex-1 overflow-y-auto">
|
|
172
|
-
<
|
|
173
|
-
{#each fieldNames as fieldName}
|
|
174
|
-
{#if !ctx.meta.collections[collectionName].fields[fieldName]?.ui?.hidden}
|
|
175
|
-
{@const field = getField(ctx, fieldName, collectionName)}
|
|
176
|
-
{@const FieldIcon = getFieldIcon(ctx, fieldName, collectionName)}
|
|
177
|
-
<div class="flex flex-col gap-2">
|
|
178
|
-
<div class="flex flex-1 items-end justify-between gap-2 text-xs">
|
|
179
|
-
<div class="flex gap-2">
|
|
180
|
-
<div class="h-fit">{field.label}</div>
|
|
181
|
-
<div class="flex h-fit items-center gap-1 text-[0.7rem] text-muted-foreground">
|
|
182
|
-
<FieldIcon size="12" />
|
|
183
|
-
{field.type}
|
|
184
|
-
</div>
|
|
185
|
-
</div>
|
|
186
|
-
<div>
|
|
187
|
-
<ExtensionsComponents
|
|
188
|
-
name="dvFields.topRight.{collectionName}.{fieldName}"
|
|
189
|
-
utils={getExtensionUtils(lobb, ctx)}
|
|
190
|
-
bind:value={entry[fieldName]}
|
|
191
|
-
/>
|
|
192
|
-
</div>
|
|
193
|
-
</div>
|
|
194
|
-
<FieldInput
|
|
195
|
-
{collectionName}
|
|
196
|
-
{fieldName}
|
|
197
|
-
bind:value={entry[fieldName]}
|
|
198
|
-
bind:entry
|
|
199
|
-
errorMessages={fieldsErrors[fieldName]}
|
|
200
|
-
/>
|
|
201
|
-
</div>
|
|
202
|
-
{/if}
|
|
203
|
-
{/each}
|
|
204
|
-
</div>
|
|
167
|
+
<DetailView {collectionName} bind:entry={values} {fieldsErrors} />
|
|
205
168
|
{#if showRelatedRecords}
|
|
206
|
-
<DetailViewChildren {collectionName} {
|
|
169
|
+
<DetailViewChildren {collectionName} entry={values} activeChanges={changes} />
|
|
207
170
|
{/if}
|
|
208
171
|
</div>
|
|
209
172
|
<div class="flex h-12 items-center justify-end gap-2 border-t px-4">
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
});
|
|
97
97
|
</script>
|
|
98
98
|
|
|
99
|
-
<div class={cn("w-full resize-y rounded-md border bg-muted
|
|
99
|
+
<div class={cn("w-full resize-y rounded-md border bg-muted-soft shadow-sm", className)}>
|
|
100
100
|
<div
|
|
101
101
|
bind:this={editorContainer}
|
|
102
102
|
class="editor pl-2"
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
placeholder={"NULL"}
|
|
86
86
|
type="number"
|
|
87
87
|
class="
|
|
88
|
-
bg-muted
|
|
88
|
+
bg-muted-soft text-xs
|
|
89
89
|
{destructive ? 'border-destructive bg-destructive/10' : ''}
|
|
90
90
|
"
|
|
91
91
|
bind:value={
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
<div class="relative z-10">
|
|
99
99
|
<Input
|
|
100
100
|
placeholder={"PARENT ID"}
|
|
101
|
-
class="bg-muted
|
|
101
|
+
class="bg-muted-soft text-xs"
|
|
102
102
|
disabled={true}
|
|
103
103
|
/>
|
|
104
104
|
</div>
|