@lobb-js/studio 0.42.0 → 0.43.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/dist/components/confirmationDialog/confirmationDialog.svelte +1 -1
- package/dist/components/dataTable/dataTable.svelte +42 -16
- package/dist/components/dataTable/listViewChildren.svelte +60 -77
- package/dist/components/dataTable/listViewChildren.svelte.d.ts +1 -1
- package/dist/components/dataTable/table.svelte +8 -56
- package/dist/components/dataTable/table.svelte.d.ts +1 -2
- package/dist/components/detailView/changeTreeUtils.d.ts +7 -0
- package/dist/components/detailView/changeTreeUtils.js +47 -0
- package/dist/components/detailView/create/createDetailView.svelte +17 -13
- package/dist/components/detailView/detailView.svelte +7 -2
- package/dist/components/detailView/detailView.svelte.d.ts +1 -0
- package/dist/components/detailView/fieldInput.svelte +10 -9
- package/dist/components/detailView/fieldInput.svelte.d.ts +1 -0
- package/dist/components/detailView/update/updateDetailView.svelte +22 -18
- package/dist/components/drawer.svelte +15 -2
- package/dist/components/foreingKeyInput.svelte +163 -68
- package/dist/components/foreingKeyInput.svelte.d.ts +1 -1
- package/dist/components/polymorphicInput.svelte +112 -63
- package/dist/components/polymorphicInput.svelte.d.ts +1 -0
- package/package.json +2 -2
- package/src/lib/components/confirmationDialog/confirmationDialog.svelte +1 -1
- package/src/lib/components/dataTable/dataTable.svelte +42 -16
- package/src/lib/components/dataTable/listViewChildren.svelte +60 -77
- package/src/lib/components/dataTable/table.svelte +8 -56
- package/src/lib/components/detailView/changeTreeUtils.ts +39 -0
- package/src/lib/components/detailView/create/createDetailView.svelte +17 -13
- package/src/lib/components/detailView/detailView.svelte +7 -2
- package/src/lib/components/detailView/fieldInput.svelte +10 -9
- package/src/lib/components/detailView/update/updateDetailView.svelte +22 -18
- package/src/lib/components/drawer.svelte +15 -2
- package/src/lib/components/foreingKeyInput.svelte +163 -68
- package/src/lib/components/polymorphicInput.svelte +112 -63
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
value: any;
|
|
25
25
|
errorMessages?: string[];
|
|
26
26
|
entry?: Record<string, any>;
|
|
27
|
+
changed?: boolean;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
let {
|
|
@@ -32,6 +33,7 @@
|
|
|
32
33
|
value = $bindable(),
|
|
33
34
|
errorMessages = [],
|
|
34
35
|
entry = $bindable(),
|
|
36
|
+
changed = false,
|
|
35
37
|
}: Props = $props();
|
|
36
38
|
|
|
37
39
|
const ui_input =
|
|
@@ -44,6 +46,7 @@
|
|
|
44
46
|
const isDisabled = field.key === 'id' || Boolean(ui?.disabled)
|
|
45
47
|
const disabledClasses = "pointer-events-none opacity-50";
|
|
46
48
|
const destructive: boolean = $derived(!isDisabled && Boolean(errorMessages.length));
|
|
49
|
+
const changedClass = $derived(changed && !destructive ? '!bg-orange-500/5' : '');
|
|
47
50
|
|
|
48
51
|
</script>
|
|
49
52
|
|
|
@@ -72,6 +75,7 @@
|
|
|
72
75
|
<PolymorphicInput
|
|
73
76
|
collectionField={polymorphicRelation.from.collection_field}
|
|
74
77
|
idField={polymorphicRelation.from.id_field}
|
|
78
|
+
virtualField={polymorphicRelation.from.virtual_field ?? ''}
|
|
75
79
|
targetCollections={polymorphicRelation.to}
|
|
76
80
|
bind:entry
|
|
77
81
|
{destructive}
|
|
@@ -87,7 +91,7 @@
|
|
|
87
91
|
{:else if field.label === "id"}
|
|
88
92
|
<Input
|
|
89
93
|
placeholder="AUTO GENERATED"
|
|
90
|
-
class="bg-muted text-xs"
|
|
94
|
+
class="bg-muted text-xs {changedClass}"
|
|
91
95
|
bind:value
|
|
92
96
|
/>
|
|
93
97
|
{:else if fieldRelationTarget && entry}
|
|
@@ -126,10 +130,7 @@
|
|
|
126
130
|
}
|
|
127
131
|
>
|
|
128
132
|
<Select.Trigger
|
|
129
|
-
class="
|
|
130
|
-
h-9 w-full bg-muted pr-8
|
|
131
|
-
{destructive ? 'border-destructive bg-destructive/10' : ''}
|
|
132
|
-
"
|
|
133
|
+
class="h-9 w-full bg-muted pr-8 {changedClass} {destructive ? 'border-destructive !bg-destructive/10' : ''}"
|
|
133
134
|
>
|
|
134
135
|
{#if value != null && enumOptions}
|
|
135
136
|
<EnumBadge value={String(value)} enum={enumOptions} />
|
|
@@ -159,7 +160,7 @@
|
|
|
159
160
|
placeholder={ui?.placeholder ? ui.placeholder : "NULL"}
|
|
160
161
|
type="text"
|
|
161
162
|
class="
|
|
162
|
-
bg-muted text-xs
|
|
163
|
+
bg-muted text-xs {changedClass}
|
|
163
164
|
{destructive ? 'border-destructive bg-destructive/10' : ''}
|
|
164
165
|
"
|
|
165
166
|
bind:value
|
|
@@ -169,7 +170,7 @@
|
|
|
169
170
|
placeholder={ui?.placeholder ? ui.placeholder : value === "" ? "EMPTY STRING" : "NULL"}
|
|
170
171
|
rows={5}
|
|
171
172
|
class="
|
|
172
|
-
bg-muted text-xs
|
|
173
|
+
bg-muted text-xs {changedClass}
|
|
173
174
|
{destructive ? 'border-destructive bg-destructive/10' : ''}
|
|
174
175
|
"
|
|
175
176
|
bind:value
|
|
@@ -265,7 +266,7 @@
|
|
|
265
266
|
scale={isFloat ? 20 : 0}
|
|
266
267
|
groupDigits={ui?.groupDigits ?? false}
|
|
267
268
|
class="
|
|
268
|
-
bg-muted text-xs
|
|
269
|
+
bg-muted text-xs {changedClass}
|
|
269
270
|
{destructive ? 'border-destructive bg-destructive/10' : ''}
|
|
270
271
|
"
|
|
271
272
|
bind:value
|
|
@@ -275,7 +276,7 @@
|
|
|
275
276
|
placeholder={ui?.placeholder ? ui.placeholder : "NULL"}
|
|
276
277
|
type="text"
|
|
277
278
|
class="
|
|
278
|
-
bg-muted text-xs
|
|
279
|
+
bg-muted text-xs {changedClass}
|
|
279
280
|
{destructive ? 'border-destructive bg-destructive/10' : ''}
|
|
280
281
|
"
|
|
281
282
|
bind:value
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
import { untrack } from "svelte";
|
|
30
30
|
import { showDialog } from "../../../actions";
|
|
31
31
|
|
|
32
|
+
|
|
32
33
|
const { lobb, ctx } = getStudioContext();
|
|
33
34
|
import { getChangedProperties } from "../../../utils";
|
|
34
35
|
import UpdateDetailViewChildren from "./updateDetailViewChildren.svelte";
|
|
@@ -81,22 +82,15 @@
|
|
|
81
82
|
const hasChildChanges = $derived(
|
|
82
83
|
Object.values(localChanges.children).some((ch: ChildrenChanges) =>
|
|
83
84
|
ch.created.length || ch.updated.length || ch.deleted.length || ch.linked.length || ch.unlinked.length
|
|
85
|
+
) ||
|
|
86
|
+
Object.values(localChanges.data).some((val: any) =>
|
|
87
|
+
val && typeof val === 'object' && !Array.isArray(val) && (val.create || val.update || val.delete)
|
|
84
88
|
)
|
|
85
89
|
);
|
|
86
90
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if (fieldCount > 0) lines.push(`${fieldCount} field${fieldCount > 1 ? 's' : ''} changed`);
|
|
91
|
-
for (const [col, ch] of Object.entries(localChanges.children) as [string, ChildrenChanges][]) {
|
|
92
|
-
if (ch.created.length) lines.push(`${ch.created.length} created in ${col}`);
|
|
93
|
-
if (ch.linked.length) lines.push(`${ch.linked.length} linked in ${col}`);
|
|
94
|
-
if (ch.updated.length) lines.push(`${ch.updated.length} edited in ${col}`);
|
|
95
|
-
if (ch.deleted.length) lines.push(`${ch.deleted.length} deleted from ${col}`);
|
|
96
|
-
if (ch.unlinked.length) lines.push(`${ch.unlinked.length} unlinked from ${col}`);
|
|
97
|
-
}
|
|
98
|
-
return lines;
|
|
99
|
-
});
|
|
91
|
+
import { buildChangeTree, renderTree } from "../changeTreeUtils";
|
|
92
|
+
|
|
93
|
+
const changeSummaryLines = $derived(renderTree(buildChangeTree(localChanges)));
|
|
100
94
|
|
|
101
95
|
$effect(() => {
|
|
102
96
|
const currentEntrySnap = $state.snapshot(values);
|
|
@@ -106,8 +100,18 @@
|
|
|
106
100
|
});
|
|
107
101
|
});
|
|
108
102
|
|
|
103
|
+
function cleanFkField(val: any): any {
|
|
104
|
+
if (!val || typeof val !== 'object' || Array.isArray(val)) return val;
|
|
105
|
+
// strip internal id from pending edit (server reads from DB)
|
|
106
|
+
if (val.id && val.update) return { ...(val.collection ? { collection: val.collection } : {}), update: val.update };
|
|
107
|
+
// { delete: true } — pass through as-is (no id needed)
|
|
108
|
+
return val;
|
|
109
|
+
}
|
|
110
|
+
|
|
109
111
|
function buildPayload(changes: Changes): { data: Record<string, any>; children?: Record<string, any> } {
|
|
110
|
-
const { id: _id, ...
|
|
112
|
+
const { id: _id, ...rawData } = changes.data;
|
|
113
|
+
const data: Record<string, any> = {};
|
|
114
|
+
for (const [key, val] of Object.entries(rawData)) data[key] = cleanFkField(val);
|
|
111
115
|
const children = buildChildren(changes.children);
|
|
112
116
|
return { data, ...(children ? { children } : {}) };
|
|
113
117
|
}
|
|
@@ -140,7 +144,7 @@
|
|
|
140
144
|
if (!isRecordingMode && hasChildChanges && changeSummaryLines.length > 0) {
|
|
141
145
|
const confirmed = await showDialog(
|
|
142
146
|
"Confirm changes",
|
|
143
|
-
changeSummaryLines.
|
|
147
|
+
changeSummaryLines.join('\n')
|
|
144
148
|
);
|
|
145
149
|
if (!confirmed) return;
|
|
146
150
|
}
|
|
@@ -151,7 +155,7 @@
|
|
|
151
155
|
if (response.status === 204) {
|
|
152
156
|
onChanges?.(snap);
|
|
153
157
|
if (onSuccessfullSave) await onSuccessfullSave(snap);
|
|
154
|
-
toast.success(`The record was successfully updated`);
|
|
158
|
+
if (!isRecordingMode) toast.success(`The record was successfully updated`);
|
|
155
159
|
onCancel?.();
|
|
156
160
|
return;
|
|
157
161
|
}
|
|
@@ -171,7 +175,7 @@
|
|
|
171
175
|
|
|
172
176
|
onChanges?.(snap);
|
|
173
177
|
if (onSuccessfullSave) await onSuccessfullSave(snap);
|
|
174
|
-
toast.success(`The record was successfully updated`);
|
|
178
|
+
if (!isRecordingMode) toast.success(`The record was successfully updated`);
|
|
175
179
|
onCancel?.();
|
|
176
180
|
}
|
|
177
181
|
</script>
|
|
@@ -196,7 +200,7 @@
|
|
|
196
200
|
</div>
|
|
197
201
|
</div>
|
|
198
202
|
<div class="flex-1 overflow-y-auto">
|
|
199
|
-
<DetailView {collectionName} bind:entry={values} {fieldsErrors} />
|
|
203
|
+
<DetailView {collectionName} bind:entry={values} {fieldsErrors} changedFields={Object.keys(localChanges.data)} />
|
|
200
204
|
{#if showRelatedRecords}
|
|
201
205
|
<UpdateDetailViewChildren {collectionName} entry={values} changes={localChanges} onChanges={(children) => { localChanges.children = children; }} />
|
|
202
206
|
{/if}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { fade } from "svelte/transition";
|
|
5
5
|
import { cubicOut } from "svelte/easing";
|
|
6
6
|
import Portal from "svelte-portal";
|
|
7
|
+
import { getContext, setContext, onDestroy } from "svelte";
|
|
7
8
|
|
|
8
9
|
interface Props {
|
|
9
10
|
children?: Snippet<[]>;
|
|
@@ -13,6 +14,16 @@
|
|
|
13
14
|
|
|
14
15
|
let { onHide, children, position = "side" }: Props = $props();
|
|
15
16
|
|
|
17
|
+
// Track nesting depth for stacking effect
|
|
18
|
+
const DEPTH_KEY = 'drawer-depth';
|
|
19
|
+
const parentDepth: number = getContext(DEPTH_KEY) ?? 0;
|
|
20
|
+
const depth = parentDepth + 1;
|
|
21
|
+
setContext(DEPTH_KEY, depth);
|
|
22
|
+
|
|
23
|
+
// Side drawers get narrower, bottom drawers get shorter — both offset by 48px per level
|
|
24
|
+
const sideWidth = $derived(calculateDrawerWidth() - (depth - 1) * 48);
|
|
25
|
+
const bottomOffset = $derived((depth - 1) * 48);
|
|
26
|
+
|
|
16
27
|
function slide(_node: Element, { duration = 250, axis }: { duration?: number; axis: "x" | "y" }) {
|
|
17
28
|
return {
|
|
18
29
|
duration,
|
|
@@ -39,9 +50,11 @@
|
|
|
39
50
|
role="dialog"
|
|
40
51
|
transition:slide={{ axis: position === "bottom" ? "y" : "x" }}
|
|
41
52
|
class={position === "bottom"
|
|
42
|
-
? "fixed bottom-0 left-0 z-40 flex
|
|
53
|
+
? "fixed bottom-0 left-0 z-40 flex w-full flex-col border-t bg-card"
|
|
43
54
|
: "fixed right-0 top-0 z-40 flex h-full w-full flex-col border-l bg-card"}
|
|
44
|
-
style={position === "side"
|
|
55
|
+
style={position === "side"
|
|
56
|
+
? `max-width: ${sideWidth}px;`
|
|
57
|
+
: `height: calc(60vh - ${bottomOffset}px);`}
|
|
45
58
|
>
|
|
46
59
|
{@render children?.()}
|
|
47
60
|
</div>
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
import { onMount } from "svelte";
|
|
3
3
|
import Input from "./ui/input/input.svelte";
|
|
4
4
|
import SelectRecord from "./selectRecord.svelte";
|
|
5
|
-
import UpdateDetailViewButton from "./detailView/update/updateDetailViewButton.svelte";
|
|
6
5
|
import CreateDetailView from "./detailView/create/createDetailView.svelte";
|
|
6
|
+
import UpdateDetailView from "./detailView/update/updateDetailView.svelte";
|
|
7
7
|
import { getCollectionPrimaryField } from "./dataTable/utils";
|
|
8
8
|
import { getStudioContext } from "../context";
|
|
9
|
-
import {
|
|
9
|
+
import { Plus, Link, Pencil, Unlink, Trash, RotateCcw, RefreshCw } from "lucide-svelte";
|
|
10
10
|
import Button from "./ui/button/button.svelte";
|
|
11
11
|
|
|
12
12
|
const { lobb, ctx } = getStudioContext();
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
parentCollectionName: string;
|
|
16
16
|
collectionName: string;
|
|
17
17
|
fieldName: string;
|
|
18
|
-
value?:
|
|
18
|
+
value?: any;
|
|
19
19
|
destructive?: boolean;
|
|
20
20
|
entry: Record<string, any>;
|
|
21
21
|
}
|
|
@@ -29,96 +29,180 @@
|
|
|
29
29
|
entry,
|
|
30
30
|
}: LocalProps = $props();
|
|
31
31
|
|
|
32
|
-
let displayName = $state<string | null>(null);
|
|
33
32
|
let createDrawerOpen = $state(false);
|
|
33
|
+
let editDrawerOpen = $state(false);
|
|
34
|
+
let editValues: Record<string, any> | undefined = $state(undefined);
|
|
35
|
+
let originalId = $state<number | null>(null);
|
|
36
|
+
let initialValue = $state<any>(undefined);
|
|
37
|
+
let unlinked = $state(false);
|
|
34
38
|
|
|
35
|
-
onMount(
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
onMount(() => { initialValue = value; });
|
|
40
|
+
|
|
41
|
+
// Derived state from value
|
|
42
|
+
const isPendingCreate = $derived(value && typeof value === 'object' && value.create);
|
|
43
|
+
const isPendingEdit = $derived(value && typeof value === 'object' && value.id && value.update);
|
|
44
|
+
const isStagedDelete = $derived(value && typeof value === 'object' && value.delete === true);
|
|
45
|
+
const isStagedUnlink = $derived(unlinked);
|
|
46
|
+
const hasRealId = $derived(value != null && typeof value === 'number');
|
|
47
|
+
const isNewLink = $derived(hasRealId && initialValue !== undefined && initialValue == null && value !== initialValue);
|
|
48
|
+
const isReplaced = $derived(hasRealId && initialValue !== undefined && initialValue != null && value !== initialValue);
|
|
49
|
+
const isEmpty = $derived((value == null || value === 0) && !unlinked);
|
|
50
|
+
const isZeroPlaceholder = $derived(value === 0);
|
|
51
|
+
|
|
52
|
+
const bgClass = $derived(
|
|
53
|
+
isPendingCreate ? '!bg-green-500/5 border-green-500/40' :
|
|
54
|
+
isNewLink ? '!bg-blue-500/5 border-blue-500/40' :
|
|
55
|
+
isReplaced ? '!bg-orange-500/5 border-orange-500/40' :
|
|
56
|
+
isPendingEdit ? '!bg-orange-500/5 border-orange-500/40' :
|
|
57
|
+
isStagedUnlink ? '!bg-slate-500/5 border-slate-500/40' :
|
|
58
|
+
isStagedDelete ? '!bg-red-500/5 border-red-500/40' :
|
|
59
|
+
''
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const displayId = $derived(
|
|
63
|
+
isPendingEdit ? (value as any).id :
|
|
64
|
+
isStagedUnlink ? originalId :
|
|
65
|
+
isStagedDelete ? originalId :
|
|
66
|
+
hasRealId ? value :
|
|
67
|
+
null
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
async function handleCreated(record: any) {
|
|
71
|
+
// dry-run result has no id — store as pending create
|
|
72
|
+
if (!record.id) {
|
|
73
|
+
value = { create: record };
|
|
74
|
+
} else {
|
|
75
|
+
value = record.id;
|
|
44
76
|
}
|
|
45
|
-
}
|
|
77
|
+
}
|
|
46
78
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
79
|
+
async function openEdit() {
|
|
80
|
+
const res = await lobb.findAll(collectionName, { filter: { id: value }, limit: 1 });
|
|
81
|
+
const result = await res.json();
|
|
82
|
+
editValues = result.data[0];
|
|
83
|
+
editDrawerOpen = true;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function handleEditChanges(changes: import('./detailView/utils').Changes) {
|
|
87
|
+
if (Object.keys(changes.data).length === 0) {
|
|
88
|
+
value = (editValues as any)?.id ?? value;
|
|
89
|
+
} else {
|
|
90
|
+
value = { id: (editValues as any)?.id ?? value, update: changes.data };
|
|
91
|
+
}
|
|
92
|
+
}
|
|
50
93
|
|
|
51
94
|
function handleSelect(selectedEntry: any) {
|
|
52
|
-
const primaryFieldName = getCollectionPrimaryField(ctx, collectionName);
|
|
53
95
|
value = selectedEntry.id;
|
|
54
|
-
displayName = primaryFieldName ? String(selectedEntry[primaryFieldName]) : null;
|
|
55
96
|
}
|
|
56
97
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
98
|
+
function handleUnlink() {
|
|
99
|
+
if (hasRealId) {
|
|
100
|
+
originalId = value as number;
|
|
101
|
+
unlinked = true;
|
|
102
|
+
value = null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function handleDelete() {
|
|
107
|
+
if (hasRealId) {
|
|
108
|
+
originalId = value as number;
|
|
109
|
+
value = { delete: true };
|
|
110
|
+
}
|
|
61
111
|
}
|
|
62
112
|
|
|
63
|
-
|
|
113
|
+
function handleRevert() {
|
|
114
|
+
if (originalId != null) {
|
|
115
|
+
value = originalId;
|
|
116
|
+
originalId = null;
|
|
117
|
+
} else {
|
|
118
|
+
value = null;
|
|
119
|
+
}
|
|
120
|
+
unlinked = false;
|
|
121
|
+
}
|
|
64
122
|
</script>
|
|
65
123
|
|
|
66
|
-
{#if !
|
|
124
|
+
{#if !isZeroPlaceholder}
|
|
67
125
|
<div class="relative">
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
126
|
+
<!-- Action buttons overlay on the right -->
|
|
127
|
+
<div class="flex gap-1 absolute right-0 top-0 mr-9 h-full items-center text-xs">
|
|
128
|
+
{#if isStagedUnlink || isStagedDelete || isPendingCreate || isPendingEdit}
|
|
129
|
+
<Button
|
|
130
|
+
class="h-5 w-5 px-0 py-0 hover:bg-transparent text-muted-foreground"
|
|
73
131
|
variant="ghost"
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
{
|
|
79
|
-
<
|
|
80
|
-
|
|
81
|
-
|
|
132
|
+
Icon={RotateCcw}
|
|
133
|
+
onclick={handleRevert}
|
|
134
|
+
title="Revert"
|
|
135
|
+
></Button>
|
|
136
|
+
{:else if hasRealId}
|
|
137
|
+
<Button
|
|
138
|
+
class="h-5 w-5 px-0 py-0 hover:bg-transparent text-muted-foreground"
|
|
139
|
+
variant="ghost"
|
|
140
|
+
Icon={Trash}
|
|
141
|
+
onclick={handleDelete}
|
|
142
|
+
title="Delete record"
|
|
143
|
+
></Button>
|
|
144
|
+
<Button
|
|
145
|
+
class="h-5 w-5 px-0 py-0 hover:bg-transparent text-muted-foreground"
|
|
146
|
+
variant="ghost"
|
|
147
|
+
Icon={Unlink}
|
|
148
|
+
onclick={handleUnlink}
|
|
149
|
+
title="Unlink"
|
|
150
|
+
></Button>
|
|
151
|
+
<Button
|
|
152
|
+
class="h-5 w-5 px-0 py-0 hover:bg-transparent text-muted-foreground"
|
|
153
|
+
variant="ghost"
|
|
154
|
+
Icon={Pencil}
|
|
155
|
+
onclick={openEdit}
|
|
156
|
+
title="Edit record"
|
|
157
|
+
></Button>
|
|
158
|
+
<SelectRecord
|
|
159
|
+
class="h-5 w-5 px-0 py-0 hover:bg-transparent text-muted-foreground"
|
|
160
|
+
variant="ghost"
|
|
161
|
+
{collectionName}
|
|
162
|
+
onSelect={handleSelect}
|
|
163
|
+
additionalFilter={{ id: { $ne: value } }}
|
|
164
|
+
title="Replace"
|
|
165
|
+
{entry}
|
|
166
|
+
>
|
|
167
|
+
{#snippet children()}<RefreshCw size="13" />{/snippet}
|
|
168
|
+
</SelectRecord>
|
|
169
|
+
{:else if isEmpty}
|
|
170
|
+
<Button
|
|
171
|
+
class="h-6 px-2 font-normal text-xs"
|
|
172
|
+
variant="outline"
|
|
173
|
+
Icon={Plus}
|
|
174
|
+
onclick={() => (createDrawerOpen = true)}
|
|
175
|
+
>
|
|
176
|
+
Create
|
|
177
|
+
</Button>
|
|
178
|
+
<SelectRecord
|
|
179
|
+
class="h-6 px-2 font-normal text-xs"
|
|
180
|
+
variant="outline"
|
|
181
|
+
{parentCollectionName}
|
|
182
|
+
{collectionName}
|
|
183
|
+
{fieldName}
|
|
184
|
+
onSelect={handleSelect}
|
|
185
|
+
text="Link"
|
|
186
|
+
{entry}
|
|
187
|
+
/>
|
|
82
188
|
{/if}
|
|
83
|
-
<Button
|
|
84
|
-
class="h-6 px-2 font-normal text-xs"
|
|
85
|
-
variant="outline"
|
|
86
|
-
Icon={Plus}
|
|
87
|
-
onclick={() => (createDrawerOpen = true)}
|
|
88
|
-
>
|
|
89
|
-
Create
|
|
90
|
-
</Button>
|
|
91
|
-
<SelectRecord
|
|
92
|
-
class="h-6 px-2 font-normal text-xs"
|
|
93
|
-
variant="outline"
|
|
94
|
-
{parentCollectionName}
|
|
95
|
-
{collectionName}
|
|
96
|
-
{fieldName}
|
|
97
|
-
onSelect={handleSelect}
|
|
98
|
-
text="Select"
|
|
99
|
-
{entry}
|
|
100
|
-
/>
|
|
101
189
|
</div>
|
|
190
|
+
|
|
191
|
+
<!-- Input field colored by state -->
|
|
102
192
|
<Input
|
|
103
|
-
placeholder={"NULL"}
|
|
193
|
+
placeholder={isEmpty ? "NULL" : ""}
|
|
104
194
|
type="number"
|
|
105
|
-
class="
|
|
106
|
-
|
|
107
|
-
{destructive ? 'border-destructive bg-destructive/10' : ''}
|
|
108
|
-
"
|
|
195
|
+
class="bg-muted text-xs {bgClass} {destructive ? '!bg-destructive/10 border-destructive' : ''}"
|
|
196
|
+
disabled={isPendingCreate || isPendingEdit}
|
|
109
197
|
bind:value={
|
|
110
|
-
() =>
|
|
111
|
-
(v) => (value = (v === "" || v == null) ? null : Number(v)
|
|
198
|
+
() => displayId ?? "",
|
|
199
|
+
(v) => { if (hasRealId || isEmpty) value = (v === "" || v == null) ? null : Number(v); }
|
|
112
200
|
}
|
|
113
201
|
/>
|
|
114
202
|
</div>
|
|
115
203
|
{:else}
|
|
116
204
|
<div class="relative z-10">
|
|
117
|
-
<Input
|
|
118
|
-
placeholder={"PARENT ID"}
|
|
119
|
-
class="bg-muted text-xs"
|
|
120
|
-
disabled={true}
|
|
121
|
-
/>
|
|
205
|
+
<Input placeholder="PARENT ID" class="bg-muted text-xs" disabled={true} />
|
|
122
206
|
</div>
|
|
123
207
|
{/if}
|
|
124
208
|
|
|
@@ -126,6 +210,17 @@
|
|
|
126
210
|
<CreateDetailView
|
|
127
211
|
collectionName={collectionName}
|
|
128
212
|
onCreated={handleCreated}
|
|
213
|
+
onChanges={() => {}}
|
|
129
214
|
onCancel={async () => { createDrawerOpen = false; }}
|
|
130
215
|
/>
|
|
131
216
|
{/if}
|
|
217
|
+
|
|
218
|
+
{#if editDrawerOpen && editValues}
|
|
219
|
+
<UpdateDetailView
|
|
220
|
+
collectionName={collectionName}
|
|
221
|
+
recordId={String(editValues.id)}
|
|
222
|
+
values={editValues}
|
|
223
|
+
onChanges={handleEditChanges}
|
|
224
|
+
onCancel={async () => { editDrawerOpen = false; editValues = undefined; }}
|
|
225
|
+
/>
|
|
226
|
+
{/if}
|