@lobb-js/studio 0.27.0 → 0.27.1

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.
@@ -64,7 +64,7 @@
64
64
  variant="ghost"
65
65
  class="h-7 px-3 text-xs font-normal"
66
66
  Icon={Plus}
67
- values={{ [child.field]: { id: recordId } }}
67
+ values={{ [child.field]: recordId }}
68
68
  onSuccessfullSave={async () => { refreshDataTable = !refreshDataTable; }}
69
69
  >
70
70
  Create
@@ -30,8 +30,6 @@
30
30
  import {
31
31
  buildChildren,
32
32
  getDefaultEntry,
33
- parseDetailViewValues,
34
- serializeEntry,
35
33
  } from "../utils";
36
34
  import { getChangedProperties } from "../../../utils";
37
35
  import type { Snippet } from "svelte";
@@ -50,8 +48,6 @@
50
48
  submitButton,
51
49
  }: CreateDetailViewProp = $props();
52
50
 
53
- parseDetailViewValues(ctx, collectionName, values)
54
-
55
51
  const fieldNames = Object.keys(ctx.meta.collections[collectionName].fields);
56
52
  let entry: Record<string, any> = $state(
57
53
  getDefaultEntry(ctx, fieldNames, collectionName, values),
@@ -88,9 +84,8 @@
88
84
  return;
89
85
  }
90
86
 
91
- const serializedEntry = serializeEntry(ctx, collectionName, localEntry);
92
87
  const children = buildChildren(ctx, collectionName, localEntry);
93
- const response = await lobb.createOne(collectionName, serializedEntry, children);
88
+ const response = await lobb.createOne(collectionName, localEntry, children);
94
89
 
95
90
  await emitEvent({ lobb, ctx }, "studio.collections.create", {
96
91
  collectionName,
@@ -31,7 +31,7 @@
31
31
  import { getField, getFieldIcon } from "../../dataTable/utils";
32
32
  import DetailViewChildren from "../update/detailViewChildren.svelte";
33
33
  import type { Snippet } from "svelte";
34
- import { getDefaultEntry, parseDetailViewValues, serializeEntry } from "../utils";
34
+ import { getDefaultEntry } from "../utils";
35
35
  import FieldInput from "../fieldInput.svelte";
36
36
  import Drawer from "../../../components/drawer.svelte";
37
37
 
@@ -46,8 +46,6 @@
46
46
  recordId,
47
47
  }: UpdateDetailViewProp = $props();
48
48
 
49
- parseDetailViewValues(ctx, collectionName, values)
50
-
51
49
  const fieldNames = Object.keys(ctx.meta.collections[collectionName].fields);
52
50
  let entry: Record<string, any> = $state(
53
51
  getDefaultEntry(ctx, fieldNames, collectionName, values),
@@ -61,7 +59,6 @@
61
59
 
62
60
  async function handleSave() {
63
61
  delete localEntry.id;
64
- localEntry = serializeEntry(ctx, collectionName, localEntry);
65
62
 
66
63
  const children = Object.keys(pendingChildren).length ? pendingChildren : undefined;
67
64
  const response = await lobb.updateOne(
@@ -4,7 +4,6 @@
4
4
  import Button from "../../../components/ui/button/button.svelte";
5
5
  import UpdateDetailView from "./updateDetailView.svelte";
6
6
  import { getStudioContext } from "../../../context";
7
- import { getCollectionParamsFields } from "../../dataTable/utils";
8
7
 
9
8
  interface LocalProp extends UpdateDetailViewProp {
10
9
  variant?: ButtonProps["variant"];
@@ -17,11 +16,11 @@
17
16
  let open = $state(false);
18
17
  let values: Record<string, any> | undefined = $state(undefined);
19
18
 
20
- const { lobb, ctx } = getStudioContext();
19
+ const { lobb } = getStudioContext();
21
20
 
22
21
  async function openView() {
23
22
  const params = {
24
- fields: getCollectionParamsFields(ctx, props.collectionName, true),
23
+ fields: "*",
25
24
  filter: { id: props.recordId },
26
25
  limit: 1,
27
26
  };
@@ -3,10 +3,8 @@ import type { DetailFormField } from "./detailViewForm.svelte";
3
3
  export declare function getDefaultEntry(ctx: CTX, fieldNames: string[], collectionName: string, values?: Record<string, any>): {
4
4
  [k: string]: any;
5
5
  };
6
- export declare function serializeEntry(ctx: CTX, collectionName: string, entry: Record<string, any>): Record<string, any>;
7
6
  export declare function buildChildren(ctx: CTX, collectionName: string, entry: Record<string, any>): Record<string, {
8
7
  create?: any[];
9
8
  link?: any[];
10
9
  }> | undefined;
11
- export declare function parseDetailViewValues(ctx: CTX, collectionName: string, values: Record<string, any>): void;
12
10
  export declare function getCollectionFields(ctx: CTX, collectionName: string): DetailFormField[];
@@ -1,16 +1,4 @@
1
- var __assign = (this && this.__assign) || function () {
2
- __assign = Object.assign || function(t) {
3
- for (var s, i = 1, n = arguments.length; i < n; i++) {
4
- s = arguments[i];
5
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
6
- t[p] = s[p];
7
- }
8
- return t;
9
- };
10
- return __assign.apply(this, arguments);
11
- };
12
1
  import Mustache from "mustache";
13
- import { isRelationField } from "../../relations";
14
2
  import { getField } from "../dataTable/utils";
15
3
  export function getDefaultEntry(ctx, fieldNames, collectionName, values) {
16
4
  return Object.fromEntries(fieldNames.map(function (fieldName) {
@@ -33,42 +21,17 @@ export function getDefaultEntry(ctx, fieldNames, collectionName, values) {
33
21
  return [fieldName, value];
34
22
  }));
35
23
  }
36
- export function serializeEntry(ctx, collectionName, entry) {
37
- entry = __assign({}, entry);
38
- // extract FK object fields → ID
39
- for (var _i = 0, _a = Object.entries(entry); _i < _a.length; _i++) {
40
- var _b = _a[_i], fieldName = _b[0], fieldValue = _b[1];
41
- var isRefrenceField = isRelationField(ctx, collectionName, fieldName);
42
- if (isRefrenceField && fieldValue !== null && fieldValue.id !== undefined) {
43
- entry[fieldName] = fieldValue.id;
44
- }
45
- }
46
- // extract polymorphic id_field objects → ID
47
- for (var _c = 0, _d = ctx.meta.relations; _c < _d.length; _c++) {
48
- var relation = _d[_c];
49
- if (relation.type !== "polymorphic")
50
- continue;
51
- if (relation.from.collection !== collectionName)
52
- continue;
53
- var idField = relation.from.id_field;
54
- var fieldValue = entry[idField];
55
- if (fieldValue !== null && typeof fieldValue === "object" && fieldValue.id !== undefined) {
56
- entry[idField] = fieldValue.id;
57
- }
58
- }
59
- return entry;
60
- }
61
24
  export function buildChildren(ctx, collectionName, entry) {
62
25
  var childrenRelations = ctx.meta.relations.filter(function (relation) { return relation.type !== "polymorphic" && relation.to.collection === collectionName; });
63
26
  var children = {};
64
- var _loop_1 = function (relation) {
27
+ for (var _i = 0, childrenRelations_1 = childrenRelations; _i < childrenRelations_1.length; _i++) {
28
+ var relation = childrenRelations_1[_i];
65
29
  var childCollection = relation.from.collection;
66
30
  var childEntries = entry[childCollection];
67
31
  if (!(childEntries === null || childEntries === void 0 ? void 0 : childEntries.length))
68
- return "continue";
32
+ continue;
69
33
  var toCreate = childEntries
70
- .filter(function (e) { return !e.id; })
71
- .map(function (e) { return serializeEntry(ctx, childCollection, e); });
34
+ .filter(function (e) { return !e.id; });
72
35
  var toLink = childEntries
73
36
  .filter(function (e) { return e.id; })
74
37
  .map(function (e) { return e.id; });
@@ -79,54 +42,15 @@ export function buildChildren(ctx, collectionName, entry) {
79
42
  if (toLink.length)
80
43
  children[childCollection].link = toLink;
81
44
  }
82
- };
83
- for (var _i = 0, childrenRelations_1 = childrenRelations; _i < childrenRelations_1.length; _i++) {
84
- var relation = childrenRelations_1[_i];
85
- _loop_1(relation);
86
45
  }
87
46
  return Object.keys(children).length ? children : undefined;
88
47
  }
89
- export function parseDetailViewValues(ctx, collectionName, values) {
90
- var forignFieldNames = ctx.meta.relations
91
- .filter(function (relation) { return relation.type !== "polymorphic" && relation.from.collection === collectionName; })
92
- .map(function (relation) { return relation.from.field; });
93
- var childCollectionNames = ctx.meta.relations
94
- .filter(function (relation) { return relation.to.collection === collectionName; })
95
- .map(function (relation) { return relation.from.collection; });
96
- for (var _i = 0, _a = Object.entries(values); _i < _a.length; _i++) {
97
- var _b = _a[_i], key = _b[0], value = _b[1];
98
- if (forignFieldNames.includes(key)) {
99
- if (typeof value === 'number') {
100
- values[key] = {
101
- id: value,
102
- };
103
- }
104
- }
105
- else if (childCollectionNames.includes(key)) {
106
- for (var index = 0; index < values[key].length; index++) {
107
- parseDetailViewValues(ctx, key, values[key][index]);
108
- }
109
- }
110
- }
111
- // wrap polymorphic id_field scalar values into { id: value }
112
- for (var _c = 0, _d = ctx.meta.relations; _c < _d.length; _c++) {
113
- var relation = _d[_c];
114
- if (relation.type !== "polymorphic")
115
- continue;
116
- if (relation.from.collection !== collectionName)
117
- continue;
118
- var idField = relation.from.id_field;
119
- if (idField in values && typeof values[idField] === 'number') {
120
- values[idField] = { id: values[idField] };
121
- }
122
- }
123
- }
124
48
  export function getCollectionFields(ctx, collectionName) {
125
49
  var returnedData = [];
126
50
  var collectionFields = ctx.meta.collections[collectionName].fields;
127
51
  var isSingleton = ctx.meta.collections[collectionName].singleton;
128
52
  for (var _i = 0, _a = Object.entries(collectionFields); _i < _a.length; _i++) {
129
- var _b = _a[_i], fieldName = _b[0], value = _b[1];
53
+ var fieldName = _a[_i][0];
130
54
  if (isSingleton && fieldName === "id") {
131
55
  continue;
132
56
  }
@@ -1,14 +1,19 @@
1
1
  <script lang="ts">
2
+ import { onMount } from "svelte";
2
3
  import Input from "./ui/input/input.svelte";
3
4
  import SelectRecord from "./selectRecord.svelte";
4
5
  import UpdateDetailViewButton from "./detailView/update/updateDetailViewButton.svelte";
6
+ import { getCollectionPrimaryField } from "./dataTable/utils";
7
+ import { getStudioContext } from "../context";
5
8
  import { ExternalLink } from "lucide-svelte";
6
9
 
10
+ const { lobb, ctx } = getStudioContext();
11
+
7
12
  interface LocalProps {
8
13
  parentCollectionName: string;
9
14
  collectionName: string;
10
15
  fieldName: string;
11
- value?: any;
16
+ value?: number | null;
12
17
  destructive?: boolean;
13
18
  entry: Record<string, any>;
14
19
  }
@@ -22,31 +27,48 @@
22
27
  entry,
23
28
  }: LocalProps = $props();
24
29
 
25
- const idIsZero = $derived(value?.id === 0);
26
- const refrenceId = $derived(value ? value.id : null);
27
- const primaryField = $derived(value ? Object.values(value)[1] : null);
30
+ let displayName = $state<string | null>(null);
31
+
32
+ onMount(async () => {
33
+ if (value == null) return;
34
+ try {
35
+ const res = await lobb.findOne(collectionName, value);
36
+ const record = (await res.json()).data;
37
+ const primaryFieldName = getCollectionPrimaryField(ctx, collectionName);
38
+ displayName = primaryFieldName ? String(record[primaryFieldName]) : null;
39
+ } catch {
40
+ displayName = null;
41
+ }
42
+ });
43
+
44
+ $effect(() => {
45
+ if (value == null) displayName = null;
46
+ });
47
+
48
+ function handleSelect(selectedEntry: any) {
49
+ const primaryFieldName = getCollectionPrimaryField(ctx, collectionName);
50
+ value = selectedEntry.id;
51
+ displayName = primaryFieldName ? String(selectedEntry[primaryFieldName]) : null;
52
+ }
53
+
54
+ const idIsZero = $derived(value === 0);
28
55
  </script>
29
56
 
30
- <!-- THE SELECT BUTTON -->
31
57
  {#if !idIsZero}
32
58
  <div class="relative">
33
- <div
34
- class="flex gap-2 absolute right-0 top-0 mr-9 h-full items-center text-xs"
35
- >
36
- {#if value !== null}
59
+ <div class="flex gap-2 absolute right-0 top-0 mr-9 h-full items-center text-xs">
60
+ {#if value != null}
37
61
  <UpdateDetailViewButton
38
62
  collectionName={collectionName}
39
- recordId={value.id}
63
+ recordId={value}
40
64
  variant="ghost"
41
65
  class="h-5 w-5 px-0 py-0 text-muted-foreground hover:bg-transparent"
42
66
  Icon={ExternalLink}
43
67
  ></UpdateDetailViewButton>
44
68
  {/if}
45
- {#if primaryField}
46
- <div
47
- class="flex items-center bg-background rounded-full border h-6 px-3 shadow-sm"
48
- >
49
- {primaryField}
69
+ {#if displayName}
70
+ <div class="flex items-center bg-background rounded-full border h-6 px-3 shadow-sm">
71
+ {displayName}
50
72
  </div>
51
73
  {/if}
52
74
  <SelectRecord
@@ -55,7 +77,7 @@
55
77
  {parentCollectionName}
56
78
  {collectionName}
57
79
  {fieldName}
58
- bind:value
80
+ onSelect={handleSelect}
59
81
  {entry}
60
82
  />
61
83
  </div>
@@ -66,7 +88,10 @@
66
88
  bg-muted/30 text-xs
67
89
  {destructive ? 'border-destructive bg-destructive/10' : ''}
68
90
  "
69
- bind:value={() => refrenceId, (v) => (value.id = v)}
91
+ bind:value={
92
+ () => value ?? "",
93
+ (v) => (value = (v === "" || v == null) ? null : Number(v))
94
+ }
70
95
  />
71
96
  </div>
72
97
  {:else}
@@ -2,7 +2,7 @@ interface LocalProps {
2
2
  parentCollectionName: string;
3
3
  collectionName: string;
4
4
  fieldName: string;
5
- value?: any;
5
+ value?: number | null;
6
6
  destructive?: boolean;
7
7
  entry: Record<string, any>;
8
8
  }
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">
2
+ import { onMount } from "svelte";
2
3
  import Button from "./ui/button/button.svelte";
3
4
  import DataTable from "./dataTable/dataTable.svelte";
4
5
  import Drawer from "./drawer.svelte";
@@ -7,7 +8,7 @@
7
8
  import { getStudioContext } from "../context";
8
9
  import { ArrowLeft, Link, ChevronDown } from "lucide-svelte";
9
10
 
10
- const { ctx } = getStudioContext();
11
+ const { ctx, lobb } = getStudioContext();
11
12
 
12
13
  interface Props {
13
14
  collectionField: string;
@@ -26,12 +27,28 @@
26
27
  }: Props = $props();
27
28
 
28
29
  const selectedCollection = $derived(entry[collectionField] ?? null);
29
- const selectedId = $derived(entry[idField]?.id ?? entry[idField] ?? null);
30
- const primaryField = $derived(entry[idField] ? Object.values(entry[idField])[1] : null);
30
+ const selectedId = $derived(entry[idField] ?? null);
31
31
 
32
+ let displayName = $state<string | null>(null);
32
33
  let collectionPopoverOpen = $state(false);
33
34
  let recordDrawerOpen = $state(false);
34
35
 
36
+ onMount(async () => {
37
+ if (selectedCollection == null || selectedId == null) return;
38
+ try {
39
+ const res = await lobb.findOne(selectedCollection, selectedId);
40
+ const record = await res.json();
41
+ const primaryFieldName = getCollectionPrimaryField(ctx, selectedCollection);
42
+ displayName = primaryFieldName ? String(record[primaryFieldName]) : null;
43
+ } catch {
44
+ displayName = null;
45
+ }
46
+ });
47
+
48
+ $effect(() => {
49
+ if (entry[idField] == null) displayName = null;
50
+ });
51
+
35
52
  function onCollectionChange(col: string) {
36
53
  collectionPopoverOpen = false;
37
54
  if (entry[collectionField] !== col) {
@@ -42,14 +59,13 @@
42
59
  function onIdChange(e: Event) {
43
60
  const raw = (e.target as HTMLInputElement).value;
44
61
  const id = raw === "" ? null : Number(raw);
45
- entry = { ...entry, [idField]: id === null ? null : { id } };
62
+ entry = { ...entry, [idField]: id };
46
63
  }
47
64
 
48
65
  function onRecordSelect(record: any) {
49
66
  const primaryFieldName = getCollectionPrimaryField(ctx, selectedCollection!);
50
- const value: any = { id: record.id };
51
- if (primaryFieldName) value[primaryFieldName] = record[primaryFieldName];
52
- entry = { ...entry, [idField]: value };
67
+ entry = { ...entry, [idField]: record.id };
68
+ displayName = primaryFieldName ? String(record[primaryFieldName]) : null;
53
69
  recordDrawerOpen = false;
54
70
  }
55
71
  </script>
@@ -93,9 +109,9 @@
93
109
  />
94
110
 
95
111
  <!-- Primary field badge -->
96
- {#if primaryField}
112
+ {#if displayName}
97
113
  <div class="flex shrink-0 items-center bg-background rounded-full border h-6 px-3 shadow-sm">
98
- {primaryField}
114
+ {displayName}
99
115
  </div>
100
116
  {/if}
101
117
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lobb-js/studio",
3
3
  "license": "UNLICENSED",
4
- "version": "0.27.0",
4
+ "version": "0.27.1",
5
5
  "type": "module",
6
6
  "publishConfig": {
7
7
  "access": "public"
@@ -42,7 +42,7 @@
42
42
  "postpublish": "./scripts/postpublish.sh"
43
43
  },
44
44
  "devDependencies": {
45
- "@lobb-js/core": "^0.30.0",
45
+ "@lobb-js/core": "^0.31.0",
46
46
  "@chromatic-com/storybook": "^4.1.2",
47
47
  "@storybook/addon-a11y": "^10.0.1",
48
48
  "@storybook/addon-docs": "^10.0.1",
@@ -64,7 +64,7 @@
64
64
  variant="ghost"
65
65
  class="h-7 px-3 text-xs font-normal"
66
66
  Icon={Plus}
67
- values={{ [child.field]: { id: recordId } }}
67
+ values={{ [child.field]: recordId }}
68
68
  onSuccessfullSave={async () => { refreshDataTable = !refreshDataTable; }}
69
69
  >
70
70
  Create
@@ -30,8 +30,6 @@
30
30
  import {
31
31
  buildChildren,
32
32
  getDefaultEntry,
33
- parseDetailViewValues,
34
- serializeEntry,
35
33
  } from "../utils";
36
34
  import { getChangedProperties } from "../../../utils";
37
35
  import type { Snippet } from "svelte";
@@ -50,8 +48,6 @@
50
48
  submitButton,
51
49
  }: CreateDetailViewProp = $props();
52
50
 
53
- parseDetailViewValues(ctx, collectionName, values)
54
-
55
51
  const fieldNames = Object.keys(ctx.meta.collections[collectionName].fields);
56
52
  let entry: Record<string, any> = $state(
57
53
  getDefaultEntry(ctx, fieldNames, collectionName, values),
@@ -88,9 +84,8 @@
88
84
  return;
89
85
  }
90
86
 
91
- const serializedEntry = serializeEntry(ctx, collectionName, localEntry);
92
87
  const children = buildChildren(ctx, collectionName, localEntry);
93
- const response = await lobb.createOne(collectionName, serializedEntry, children);
88
+ const response = await lobb.createOne(collectionName, localEntry, children);
94
89
 
95
90
  await emitEvent({ lobb, ctx }, "studio.collections.create", {
96
91
  collectionName,
@@ -31,7 +31,7 @@
31
31
  import { getField, getFieldIcon } from "../../dataTable/utils";
32
32
  import DetailViewChildren from "../update/detailViewChildren.svelte";
33
33
  import type { Snippet } from "svelte";
34
- import { getDefaultEntry, parseDetailViewValues, serializeEntry } from "../utils";
34
+ import { getDefaultEntry } from "../utils";
35
35
  import FieldInput from "../fieldInput.svelte";
36
36
  import Drawer from "../../../components/drawer.svelte";
37
37
 
@@ -46,8 +46,6 @@
46
46
  recordId,
47
47
  }: UpdateDetailViewProp = $props();
48
48
 
49
- parseDetailViewValues(ctx, collectionName, values)
50
-
51
49
  const fieldNames = Object.keys(ctx.meta.collections[collectionName].fields);
52
50
  let entry: Record<string, any> = $state(
53
51
  getDefaultEntry(ctx, fieldNames, collectionName, values),
@@ -61,7 +59,6 @@
61
59
 
62
60
  async function handleSave() {
63
61
  delete localEntry.id;
64
- localEntry = serializeEntry(ctx, collectionName, localEntry);
65
62
 
66
63
  const children = Object.keys(pendingChildren).length ? pendingChildren : undefined;
67
64
  const response = await lobb.updateOne(
@@ -4,7 +4,6 @@
4
4
  import Button from "../../../components/ui/button/button.svelte";
5
5
  import UpdateDetailView from "./updateDetailView.svelte";
6
6
  import { getStudioContext } from "../../../context";
7
- import { getCollectionParamsFields } from "../../dataTable/utils";
8
7
 
9
8
  interface LocalProp extends UpdateDetailViewProp {
10
9
  variant?: ButtonProps["variant"];
@@ -17,11 +16,11 @@
17
16
  let open = $state(false);
18
17
  let values: Record<string, any> | undefined = $state(undefined);
19
18
 
20
- const { lobb, ctx } = getStudioContext();
19
+ const { lobb } = getStudioContext();
21
20
 
22
21
  async function openView() {
23
22
  const params = {
24
- fields: getCollectionParamsFields(ctx, props.collectionName, true),
23
+ fields: "*",
25
24
  filter: { id: props.recordId },
26
25
  limit: 1,
27
26
  };
@@ -1,6 +1,5 @@
1
1
  import Mustache from "mustache";
2
2
  import type { CTX } from "../../store.types";
3
- import { isRelationField } from "../../relations";
4
3
  import { getField } from "../dataTable/utils";
5
4
  import type { DetailFormField } from "./detailViewForm.svelte";
6
5
 
@@ -26,34 +25,6 @@ export function getDefaultEntry(ctx: CTX, fieldNames: string[], collectionName:
26
25
  );
27
26
  }
28
27
 
29
- export function serializeEntry(
30
- ctx: CTX,
31
- collectionName: string,
32
- entry: Record<string, any>,
33
- ) {
34
- entry = { ...entry }
35
-
36
- // extract FK object fields → ID
37
- for (const [fieldName, fieldValue] of Object.entries(entry)) {
38
- const isRefrenceField = isRelationField(ctx, collectionName, fieldName);
39
- if (isRefrenceField && fieldValue !== null && fieldValue.id !== undefined) {
40
- entry[fieldName] = fieldValue.id;
41
- }
42
- }
43
-
44
- // extract polymorphic id_field objects → ID
45
- for (const relation of ctx.meta.relations) {
46
- if (relation.type !== "polymorphic") continue;
47
- if ((relation as any).from.collection !== collectionName) continue;
48
- const idField = (relation as any).from.id_field;
49
- const fieldValue = entry[idField];
50
- if (fieldValue !== null && typeof fieldValue === "object" && fieldValue.id !== undefined) {
51
- entry[idField] = fieldValue.id;
52
- }
53
- }
54
-
55
- return entry;
56
- }
57
28
 
58
29
  export function buildChildren(
59
30
  ctx: CTX,
@@ -72,8 +43,7 @@ export function buildChildren(
72
43
  if (!childEntries?.length) continue;
73
44
 
74
45
  const toCreate = childEntries
75
- .filter((e) => !e.id)
76
- .map((e) => serializeEntry(ctx, childCollection, e));
46
+ .filter((e) => !e.id);
77
47
  const toLink = childEntries
78
48
  .filter((e) => e.id)
79
49
  .map((e) => e.id);
@@ -88,45 +58,13 @@ export function buildChildren(
88
58
  return Object.keys(children).length ? children : undefined;
89
59
  }
90
60
 
91
- export function parseDetailViewValues(ctx: CTX, collectionName: string, values: Record<string, any>) {
92
- const forignFieldNames = ctx.meta.relations
93
- .filter((relation) => relation.type !== "polymorphic" && relation.from.collection === collectionName)
94
- .map((relation) => relation.from.field);
95
- const childCollectionNames = ctx.meta.relations
96
- .filter((relation) => relation.to.collection === collectionName)
97
- .map((relation) => relation.from.collection);
98
-
99
- for (const [key, value] of Object.entries(values)) {
100
- if (forignFieldNames.includes(key)) {
101
- if (typeof value === 'number') {
102
- values[key] = {
103
- id: value,
104
- }
105
- }
106
- } else if (childCollectionNames.includes(key)) {
107
- for (let index = 0; index < values[key].length; index++) {
108
- parseDetailViewValues(ctx, key, values[key][index]);
109
- }
110
- }
111
- }
112
-
113
- // wrap polymorphic id_field scalar values into { id: value }
114
- for (const relation of ctx.meta.relations) {
115
- if (relation.type !== "polymorphic") continue;
116
- if ((relation as any).from.collection !== collectionName) continue;
117
- const idField = (relation as any).from.id_field;
118
- if (idField in values && typeof values[idField] === 'number') {
119
- values[idField] = { id: values[idField] };
120
- }
121
- }
122
- }
123
61
 
124
62
  export function getCollectionFields(ctx: CTX, collectionName: string) {
125
63
  let returnedData: DetailFormField[] = [];
126
64
 
127
65
  const collectionFields = ctx.meta.collections[collectionName].fields;
128
66
  const isSingleton = ctx.meta.collections[collectionName].singleton;
129
- for (const [fieldName, value] of Object.entries(collectionFields)) {
67
+ for (const [fieldName] of Object.entries(collectionFields)) {
130
68
 
131
69
  if (isSingleton && fieldName === "id") {
132
70
  continue;
@@ -1,14 +1,19 @@
1
1
  <script lang="ts">
2
+ import { onMount } from "svelte";
2
3
  import Input from "./ui/input/input.svelte";
3
4
  import SelectRecord from "./selectRecord.svelte";
4
5
  import UpdateDetailViewButton from "./detailView/update/updateDetailViewButton.svelte";
6
+ import { getCollectionPrimaryField } from "./dataTable/utils";
7
+ import { getStudioContext } from "../context";
5
8
  import { ExternalLink } from "lucide-svelte";
6
9
 
10
+ const { lobb, ctx } = getStudioContext();
11
+
7
12
  interface LocalProps {
8
13
  parentCollectionName: string;
9
14
  collectionName: string;
10
15
  fieldName: string;
11
- value?: any;
16
+ value?: number | null;
12
17
  destructive?: boolean;
13
18
  entry: Record<string, any>;
14
19
  }
@@ -22,31 +27,48 @@
22
27
  entry,
23
28
  }: LocalProps = $props();
24
29
 
25
- const idIsZero = $derived(value?.id === 0);
26
- const refrenceId = $derived(value ? value.id : null);
27
- const primaryField = $derived(value ? Object.values(value)[1] : null);
30
+ let displayName = $state<string | null>(null);
31
+
32
+ onMount(async () => {
33
+ if (value == null) return;
34
+ try {
35
+ const res = await lobb.findOne(collectionName, value);
36
+ const record = (await res.json()).data;
37
+ const primaryFieldName = getCollectionPrimaryField(ctx, collectionName);
38
+ displayName = primaryFieldName ? String(record[primaryFieldName]) : null;
39
+ } catch {
40
+ displayName = null;
41
+ }
42
+ });
43
+
44
+ $effect(() => {
45
+ if (value == null) displayName = null;
46
+ });
47
+
48
+ function handleSelect(selectedEntry: any) {
49
+ const primaryFieldName = getCollectionPrimaryField(ctx, collectionName);
50
+ value = selectedEntry.id;
51
+ displayName = primaryFieldName ? String(selectedEntry[primaryFieldName]) : null;
52
+ }
53
+
54
+ const idIsZero = $derived(value === 0);
28
55
  </script>
29
56
 
30
- <!-- THE SELECT BUTTON -->
31
57
  {#if !idIsZero}
32
58
  <div class="relative">
33
- <div
34
- class="flex gap-2 absolute right-0 top-0 mr-9 h-full items-center text-xs"
35
- >
36
- {#if value !== null}
59
+ <div class="flex gap-2 absolute right-0 top-0 mr-9 h-full items-center text-xs">
60
+ {#if value != null}
37
61
  <UpdateDetailViewButton
38
62
  collectionName={collectionName}
39
- recordId={value.id}
63
+ recordId={value}
40
64
  variant="ghost"
41
65
  class="h-5 w-5 px-0 py-0 text-muted-foreground hover:bg-transparent"
42
66
  Icon={ExternalLink}
43
67
  ></UpdateDetailViewButton>
44
68
  {/if}
45
- {#if primaryField}
46
- <div
47
- class="flex items-center bg-background rounded-full border h-6 px-3 shadow-sm"
48
- >
49
- {primaryField}
69
+ {#if displayName}
70
+ <div class="flex items-center bg-background rounded-full border h-6 px-3 shadow-sm">
71
+ {displayName}
50
72
  </div>
51
73
  {/if}
52
74
  <SelectRecord
@@ -55,7 +77,7 @@
55
77
  {parentCollectionName}
56
78
  {collectionName}
57
79
  {fieldName}
58
- bind:value
80
+ onSelect={handleSelect}
59
81
  {entry}
60
82
  />
61
83
  </div>
@@ -66,7 +88,10 @@
66
88
  bg-muted/30 text-xs
67
89
  {destructive ? 'border-destructive bg-destructive/10' : ''}
68
90
  "
69
- bind:value={() => refrenceId, (v) => (value.id = v)}
91
+ bind:value={
92
+ () => value ?? "",
93
+ (v) => (value = (v === "" || v == null) ? null : Number(v))
94
+ }
70
95
  />
71
96
  </div>
72
97
  {:else}
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">
2
+ import { onMount } from "svelte";
2
3
  import Button from "./ui/button/button.svelte";
3
4
  import DataTable from "./dataTable/dataTable.svelte";
4
5
  import Drawer from "./drawer.svelte";
@@ -7,7 +8,7 @@
7
8
  import { getStudioContext } from "../context";
8
9
  import { ArrowLeft, Link, ChevronDown } from "lucide-svelte";
9
10
 
10
- const { ctx } = getStudioContext();
11
+ const { ctx, lobb } = getStudioContext();
11
12
 
12
13
  interface Props {
13
14
  collectionField: string;
@@ -26,12 +27,28 @@
26
27
  }: Props = $props();
27
28
 
28
29
  const selectedCollection = $derived(entry[collectionField] ?? null);
29
- const selectedId = $derived(entry[idField]?.id ?? entry[idField] ?? null);
30
- const primaryField = $derived(entry[idField] ? Object.values(entry[idField])[1] : null);
30
+ const selectedId = $derived(entry[idField] ?? null);
31
31
 
32
+ let displayName = $state<string | null>(null);
32
33
  let collectionPopoverOpen = $state(false);
33
34
  let recordDrawerOpen = $state(false);
34
35
 
36
+ onMount(async () => {
37
+ if (selectedCollection == null || selectedId == null) return;
38
+ try {
39
+ const res = await lobb.findOne(selectedCollection, selectedId);
40
+ const record = await res.json();
41
+ const primaryFieldName = getCollectionPrimaryField(ctx, selectedCollection);
42
+ displayName = primaryFieldName ? String(record[primaryFieldName]) : null;
43
+ } catch {
44
+ displayName = null;
45
+ }
46
+ });
47
+
48
+ $effect(() => {
49
+ if (entry[idField] == null) displayName = null;
50
+ });
51
+
35
52
  function onCollectionChange(col: string) {
36
53
  collectionPopoverOpen = false;
37
54
  if (entry[collectionField] !== col) {
@@ -42,14 +59,13 @@
42
59
  function onIdChange(e: Event) {
43
60
  const raw = (e.target as HTMLInputElement).value;
44
61
  const id = raw === "" ? null : Number(raw);
45
- entry = { ...entry, [idField]: id === null ? null : { id } };
62
+ entry = { ...entry, [idField]: id };
46
63
  }
47
64
 
48
65
  function onRecordSelect(record: any) {
49
66
  const primaryFieldName = getCollectionPrimaryField(ctx, selectedCollection!);
50
- const value: any = { id: record.id };
51
- if (primaryFieldName) value[primaryFieldName] = record[primaryFieldName];
52
- entry = { ...entry, [idField]: value };
67
+ entry = { ...entry, [idField]: record.id };
68
+ displayName = primaryFieldName ? String(record[primaryFieldName]) : null;
53
69
  recordDrawerOpen = false;
54
70
  }
55
71
  </script>
@@ -93,9 +109,9 @@
93
109
  />
94
110
 
95
111
  <!-- Primary field badge -->
96
- {#if primaryField}
112
+ {#if displayName}
97
113
  <div class="flex shrink-0 items-center bg-background rounded-full border h-6 px-3 shadow-sm">
98
- {primaryField}
114
+ {displayName}
99
115
  </div>
100
116
  {/if}
101
117