@lobb-js/studio 0.13.0 → 0.14.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/dataTable/dataTable.svelte +81 -75
- package/dist/components/dataTable/header.svelte +8 -0
- package/dist/components/singletone.svelte +15 -20
- package/dist/extensions/extension.types.d.ts +1 -1
- package/package.json +1 -1
- package/src/lib/components/dataTable/dataTable.svelte +81 -75
- package/src/lib/components/dataTable/header.svelte +8 -0
- package/src/lib/components/singletone.svelte +15 -20
- package/src/lib/extensions/extension.types.ts +2 -1
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
import ExtensionsComponents from "../extensionsComponents.svelte";
|
|
19
19
|
import { getExtensionUtils, loadExtensionComponents } from "../../extensions/extensionUtils";
|
|
20
20
|
import Tabs from "./dataTableTabs.svelte";
|
|
21
|
+
import { fade } from "svelte/transition";
|
|
21
22
|
|
|
22
23
|
const { lobb, ctx } = getStudioContext();
|
|
23
24
|
|
|
@@ -84,6 +85,7 @@
|
|
|
84
85
|
});
|
|
85
86
|
|
|
86
87
|
async function loadData(params: any) {
|
|
88
|
+
loading = true;
|
|
87
89
|
// parsing sort before sending the request
|
|
88
90
|
const paramsCopy = $state.snapshot(params);
|
|
89
91
|
const sort: TableProps["sort"] = paramsCopy.sort;
|
|
@@ -154,7 +156,6 @@
|
|
|
154
156
|
/>
|
|
155
157
|
{/snippet}
|
|
156
158
|
|
|
157
|
-
<Tabs {collectionName} {filter} bind:activeTabFilter />
|
|
158
159
|
{#if showHeader}
|
|
159
160
|
<Header bind:params {collectionName} bind:selectedRecords>
|
|
160
161
|
{#snippet left()}
|
|
@@ -162,83 +163,88 @@
|
|
|
162
163
|
{/snippet}
|
|
163
164
|
</Header>
|
|
164
165
|
{/if}
|
|
166
|
+
<Tabs {collectionName} {filter} bind:activeTabFilter />
|
|
165
167
|
<div class="relative flex-1 overflow-auto w-full">
|
|
166
|
-
{#
|
|
167
|
-
<div class="
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
{
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
{
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
Icon={Pencil}
|
|
193
|
-
onSuccessfullSave={async () => {
|
|
194
|
-
params = { ...params };
|
|
195
|
-
}}
|
|
196
|
-
></UpdateDetailViewButton>
|
|
197
|
-
{#if showDelete}
|
|
198
|
-
<Button
|
|
199
|
-
class="h-6 w-6 text-muted-foreground hover:bg-transparent"
|
|
200
|
-
variant="ghost"
|
|
201
|
-
size="icon"
|
|
202
|
-
onclick={() => handleDelete(entry.id)}
|
|
203
|
-
Icon={Trash}
|
|
204
|
-
></Button>
|
|
205
|
-
{/if}
|
|
206
|
-
{#await getWorkflowTools($state.snapshot(entry))}
|
|
207
|
-
<div></div>
|
|
208
|
-
{:then workflowTools}
|
|
209
|
-
{#each workflowTools as workflowTool}
|
|
210
|
-
<Button
|
|
168
|
+
{#key activeTabFilter}
|
|
169
|
+
<div class="h-full w-full" in:fade={{ duration: 120 }}>
|
|
170
|
+
{#if loading}
|
|
171
|
+
<div class="flex flex-col gap-2 p-2 w-full">
|
|
172
|
+
<Skeleton class="h-8 w-full" />
|
|
173
|
+
<Skeleton class="h-8 w-[80%]" />
|
|
174
|
+
<Skeleton class="h-8 w-[60%]" />
|
|
175
|
+
</div>
|
|
176
|
+
{:else}
|
|
177
|
+
<Table
|
|
178
|
+
{data}
|
|
179
|
+
{columns}
|
|
180
|
+
showCollapsible={doesCollectionHasChildren}
|
|
181
|
+
selectByColumn="id"
|
|
182
|
+
showLastRowBorder={true}
|
|
183
|
+
showLastColumnBorder={true}
|
|
184
|
+
bind:sort={params.sort}
|
|
185
|
+
bind:selectedRecords
|
|
186
|
+
{unifiedBgColor}
|
|
187
|
+
bind:tableWidth={dataTableWidth}
|
|
188
|
+
{...tableProps}
|
|
189
|
+
rowActions={hasRowActions ? rowActionsSnippet : undefined}>
|
|
190
|
+
{#snippet tools(entry)}
|
|
191
|
+
<UpdateDetailViewButton
|
|
192
|
+
{collectionName}
|
|
193
|
+
recordId={entry.id}
|
|
211
194
|
variant="ghost"
|
|
212
195
|
class="h-5 w-5 px-0 py-0 text-muted-foreground hover:bg-transparent"
|
|
213
|
-
Icon={
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
></
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
196
|
+
Icon={Pencil}
|
|
197
|
+
onSuccessfullSave={async () => {
|
|
198
|
+
params = { ...params };
|
|
199
|
+
}}
|
|
200
|
+
></UpdateDetailViewButton>
|
|
201
|
+
{#if showDelete}
|
|
202
|
+
<Button
|
|
203
|
+
class="h-6 w-6 text-muted-foreground hover:bg-transparent"
|
|
204
|
+
variant="ghost"
|
|
205
|
+
size="icon"
|
|
206
|
+
onclick={() => handleDelete(entry.id)}
|
|
207
|
+
Icon={Trash}
|
|
208
|
+
></Button>
|
|
209
|
+
{/if}
|
|
210
|
+
{#await getWorkflowTools($state.snapshot(entry))}
|
|
211
|
+
<div></div>
|
|
212
|
+
{:then workflowTools}
|
|
213
|
+
{#each workflowTools as workflowTool}
|
|
214
|
+
<Button
|
|
215
|
+
variant="ghost"
|
|
216
|
+
class="h-5 w-5 px-0 py-0 text-muted-foreground hover:bg-transparent"
|
|
217
|
+
Icon={icons[
|
|
218
|
+
workflowTool.icon as keyof typeof icons
|
|
219
|
+
]}
|
|
220
|
+
onclick={workflowTool.onclick}
|
|
221
|
+
></Button>
|
|
222
|
+
{/each}
|
|
223
|
+
{/await}
|
|
224
|
+
{/snippet}
|
|
225
|
+
{#snippet overrideCell(value, column, entry)}
|
|
226
|
+
<FieldCell
|
|
227
|
+
{collectionName}
|
|
228
|
+
fieldName={column.id}
|
|
229
|
+
{value}
|
|
230
|
+
{entry}
|
|
231
|
+
tableParams={params}
|
|
232
|
+
/>
|
|
233
|
+
{/snippet}
|
|
234
|
+
{#snippet collapsible(entry)}
|
|
235
|
+
<ChildRecords
|
|
236
|
+
{collectionName}
|
|
237
|
+
recordId={entry.id}
|
|
238
|
+
width={dataTableWidth > dataTableContainerWidth
|
|
239
|
+
? dataTableContainerWidth
|
|
240
|
+
: dataTableWidth}
|
|
241
|
+
unifiedBgColor={unifiedBgColor ?? "bg-background"}
|
|
242
|
+
/>
|
|
243
|
+
{/snippet}
|
|
244
|
+
</Table>
|
|
245
|
+
{/if}
|
|
246
|
+
</div>
|
|
247
|
+
{/key}
|
|
242
248
|
</div>
|
|
243
249
|
{#if showFooter}
|
|
244
250
|
<Footer
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
import CreateManyButton from "../createManyButton.svelte";
|
|
11
11
|
import { showDialog } from "../confirmationDialog/store.svelte";
|
|
12
12
|
import CreateDetailViewButton from "../detailView/create/createDetailViewButton.svelte";
|
|
13
|
+
import ExtensionsComponents from "../extensionsComponents.svelte";
|
|
14
|
+
import { getExtensionUtils } from "../../extensions/extensionUtils";
|
|
13
15
|
import type { Snippet } from "svelte";
|
|
14
16
|
|
|
15
17
|
interface Props {
|
|
@@ -142,6 +144,12 @@
|
|
|
142
144
|
Icon={SquareStack}
|
|
143
145
|
onSuccessfullSave={() => (params = { ...params })}
|
|
144
146
|
></CreateManyButton>
|
|
147
|
+
<ExtensionsComponents
|
|
148
|
+
name="listView.header.actions"
|
|
149
|
+
utils={getExtensionUtils(lobb, ctx)}
|
|
150
|
+
{collectionName}
|
|
151
|
+
refresh={() => { params = { ...params }; }}
|
|
152
|
+
/>
|
|
145
153
|
<CreateDetailViewButton
|
|
146
154
|
{collectionName}
|
|
147
155
|
variant="default"
|
|
@@ -6,30 +6,27 @@
|
|
|
6
6
|
import Button from "./ui/button/button.svelte";
|
|
7
7
|
import { onMount } from "svelte";
|
|
8
8
|
import { getStudioContext } from "../context";
|
|
9
|
+
import { toast } from "svelte-sonner";
|
|
10
|
+
import Skeleton from "./ui/skeleton/skeleton.svelte";
|
|
9
11
|
|
|
10
12
|
const { lobb, ctx } = getStudioContext();
|
|
11
|
-
import { toast } from "svelte-sonner";
|
|
12
|
-
import Skeleton from "./ui/skeleton/skeleton.svelte";
|
|
13
13
|
|
|
14
14
|
interface Props {
|
|
15
15
|
collectionName: string;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
let {
|
|
19
|
-
collectionName,
|
|
20
|
-
}: Props = $props();
|
|
18
|
+
let { collectionName }: Props = $props();
|
|
21
19
|
|
|
22
20
|
let entry = $state({});
|
|
23
21
|
let loading = $state(true);
|
|
24
|
-
let singletonExists = $state(true);
|
|
25
22
|
const formFields = getCollectionFields(ctx, collectionName);
|
|
26
23
|
|
|
27
24
|
onMount(async () => {
|
|
28
|
-
const result = await lobb.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
25
|
+
const result = await fetch(`${lobb.lobbUrl}/api/collections/${collectionName}/singleton`, {
|
|
26
|
+
headers: lobb.getHeaders() as HeadersInit,
|
|
27
|
+
});
|
|
28
|
+
if (result.status !== 404) {
|
|
29
|
+
const json = await result.json();
|
|
33
30
|
entry = json.data;
|
|
34
31
|
}
|
|
35
32
|
loading = false;
|
|
@@ -43,29 +40,27 @@
|
|
|
43
40
|
}
|
|
44
41
|
</script>
|
|
45
42
|
|
|
46
|
-
<div>
|
|
47
|
-
<div class="flex justify-between items-center gap-2 p-2 border-b
|
|
43
|
+
<div class="flex flex-col h-full bg-background">
|
|
44
|
+
<div class="flex justify-between items-center gap-2 p-2 border-b h-10 shrink-0">
|
|
48
45
|
<div class="flex items-center gap-1">
|
|
49
46
|
<SidebarTrigger />
|
|
50
47
|
</div>
|
|
51
48
|
<div>
|
|
52
|
-
<Button
|
|
53
|
-
class="h-7 px-2 font-normal text-xs"
|
|
54
|
-
Icon={Save}
|
|
55
|
-
onclick={handleSave}
|
|
56
|
-
>
|
|
49
|
+
<Button class="h-7 px-2 font-normal text-xs" Icon={Save} onclick={handleSave}>
|
|
57
50
|
Save
|
|
58
51
|
</Button>
|
|
59
52
|
</div>
|
|
60
53
|
</div>
|
|
61
54
|
|
|
62
55
|
{#if loading}
|
|
63
|
-
<div class="flex flex-col gap-2 p-
|
|
56
|
+
<div class="flex flex-col gap-2 p-4 w-full">
|
|
64
57
|
<Skeleton class="h-8 w-full" />
|
|
65
58
|
<Skeleton class="h-8 w-[80%]" />
|
|
66
59
|
<Skeleton class="h-8 w-[60%]" />
|
|
67
60
|
</div>
|
|
68
61
|
{:else}
|
|
69
|
-
<
|
|
62
|
+
<div class="flex-1 overflow-y-auto max-w-xl">
|
|
63
|
+
<DetailViewForm bind:value={entry} fields={formFields} />
|
|
64
|
+
</div>
|
|
70
65
|
{/if}
|
|
71
66
|
</div>
|
|
@@ -73,7 +73,7 @@ export interface ExtensionProps {
|
|
|
73
73
|
utils: ExtensionUtils;
|
|
74
74
|
[key: string]: any;
|
|
75
75
|
}
|
|
76
|
-
export type ExtensionComponentKey = `pages.${string}` | "studio.listView" | `dvFields.topRight.${string}.${string}` | `detailView.update.subRecords.${string}` | `detailView.create.subRecords.${string}` | `detailView.fields.topRight.${string}.${string}` | `detailView.fields.foreignKey.${string}` | `listView.entry.children.${string}` | `listView.entry.actions`;
|
|
76
|
+
export type ExtensionComponentKey = `pages.${string}` | "studio.listView" | `dvFields.topRight.${string}.${string}` | `detailView.update.subRecords.${string}` | `detailView.create.subRecords.${string}` | `detailView.fields.topRight.${string}.${string}` | `detailView.fields.foreignKey.${string}` | `listView.entry.children.${string}` | `listView.entry.actions` | `listView.header.actions`;
|
|
77
77
|
export type ExtensionComponent = any | {
|
|
78
78
|
component: any;
|
|
79
79
|
when: (props: Record<string, any>) => boolean;
|
package/package.json
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
import ExtensionsComponents from "../extensionsComponents.svelte";
|
|
19
19
|
import { getExtensionUtils, loadExtensionComponents } from "../../extensions/extensionUtils";
|
|
20
20
|
import Tabs from "./dataTableTabs.svelte";
|
|
21
|
+
import { fade } from "svelte/transition";
|
|
21
22
|
|
|
22
23
|
const { lobb, ctx } = getStudioContext();
|
|
23
24
|
|
|
@@ -84,6 +85,7 @@
|
|
|
84
85
|
});
|
|
85
86
|
|
|
86
87
|
async function loadData(params: any) {
|
|
88
|
+
loading = true;
|
|
87
89
|
// parsing sort before sending the request
|
|
88
90
|
const paramsCopy = $state.snapshot(params);
|
|
89
91
|
const sort: TableProps["sort"] = paramsCopy.sort;
|
|
@@ -154,7 +156,6 @@
|
|
|
154
156
|
/>
|
|
155
157
|
{/snippet}
|
|
156
158
|
|
|
157
|
-
<Tabs {collectionName} {filter} bind:activeTabFilter />
|
|
158
159
|
{#if showHeader}
|
|
159
160
|
<Header bind:params {collectionName} bind:selectedRecords>
|
|
160
161
|
{#snippet left()}
|
|
@@ -162,83 +163,88 @@
|
|
|
162
163
|
{/snippet}
|
|
163
164
|
</Header>
|
|
164
165
|
{/if}
|
|
166
|
+
<Tabs {collectionName} {filter} bind:activeTabFilter />
|
|
165
167
|
<div class="relative flex-1 overflow-auto w-full">
|
|
166
|
-
{#
|
|
167
|
-
<div class="
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
{
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
{
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
Icon={Pencil}
|
|
193
|
-
onSuccessfullSave={async () => {
|
|
194
|
-
params = { ...params };
|
|
195
|
-
}}
|
|
196
|
-
></UpdateDetailViewButton>
|
|
197
|
-
{#if showDelete}
|
|
198
|
-
<Button
|
|
199
|
-
class="h-6 w-6 text-muted-foreground hover:bg-transparent"
|
|
200
|
-
variant="ghost"
|
|
201
|
-
size="icon"
|
|
202
|
-
onclick={() => handleDelete(entry.id)}
|
|
203
|
-
Icon={Trash}
|
|
204
|
-
></Button>
|
|
205
|
-
{/if}
|
|
206
|
-
{#await getWorkflowTools($state.snapshot(entry))}
|
|
207
|
-
<div></div>
|
|
208
|
-
{:then workflowTools}
|
|
209
|
-
{#each workflowTools as workflowTool}
|
|
210
|
-
<Button
|
|
168
|
+
{#key activeTabFilter}
|
|
169
|
+
<div class="h-full w-full" in:fade={{ duration: 120 }}>
|
|
170
|
+
{#if loading}
|
|
171
|
+
<div class="flex flex-col gap-2 p-2 w-full">
|
|
172
|
+
<Skeleton class="h-8 w-full" />
|
|
173
|
+
<Skeleton class="h-8 w-[80%]" />
|
|
174
|
+
<Skeleton class="h-8 w-[60%]" />
|
|
175
|
+
</div>
|
|
176
|
+
{:else}
|
|
177
|
+
<Table
|
|
178
|
+
{data}
|
|
179
|
+
{columns}
|
|
180
|
+
showCollapsible={doesCollectionHasChildren}
|
|
181
|
+
selectByColumn="id"
|
|
182
|
+
showLastRowBorder={true}
|
|
183
|
+
showLastColumnBorder={true}
|
|
184
|
+
bind:sort={params.sort}
|
|
185
|
+
bind:selectedRecords
|
|
186
|
+
{unifiedBgColor}
|
|
187
|
+
bind:tableWidth={dataTableWidth}
|
|
188
|
+
{...tableProps}
|
|
189
|
+
rowActions={hasRowActions ? rowActionsSnippet : undefined}>
|
|
190
|
+
{#snippet tools(entry)}
|
|
191
|
+
<UpdateDetailViewButton
|
|
192
|
+
{collectionName}
|
|
193
|
+
recordId={entry.id}
|
|
211
194
|
variant="ghost"
|
|
212
195
|
class="h-5 w-5 px-0 py-0 text-muted-foreground hover:bg-transparent"
|
|
213
|
-
Icon={
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
></
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
196
|
+
Icon={Pencil}
|
|
197
|
+
onSuccessfullSave={async () => {
|
|
198
|
+
params = { ...params };
|
|
199
|
+
}}
|
|
200
|
+
></UpdateDetailViewButton>
|
|
201
|
+
{#if showDelete}
|
|
202
|
+
<Button
|
|
203
|
+
class="h-6 w-6 text-muted-foreground hover:bg-transparent"
|
|
204
|
+
variant="ghost"
|
|
205
|
+
size="icon"
|
|
206
|
+
onclick={() => handleDelete(entry.id)}
|
|
207
|
+
Icon={Trash}
|
|
208
|
+
></Button>
|
|
209
|
+
{/if}
|
|
210
|
+
{#await getWorkflowTools($state.snapshot(entry))}
|
|
211
|
+
<div></div>
|
|
212
|
+
{:then workflowTools}
|
|
213
|
+
{#each workflowTools as workflowTool}
|
|
214
|
+
<Button
|
|
215
|
+
variant="ghost"
|
|
216
|
+
class="h-5 w-5 px-0 py-0 text-muted-foreground hover:bg-transparent"
|
|
217
|
+
Icon={icons[
|
|
218
|
+
workflowTool.icon as keyof typeof icons
|
|
219
|
+
]}
|
|
220
|
+
onclick={workflowTool.onclick}
|
|
221
|
+
></Button>
|
|
222
|
+
{/each}
|
|
223
|
+
{/await}
|
|
224
|
+
{/snippet}
|
|
225
|
+
{#snippet overrideCell(value, column, entry)}
|
|
226
|
+
<FieldCell
|
|
227
|
+
{collectionName}
|
|
228
|
+
fieldName={column.id}
|
|
229
|
+
{value}
|
|
230
|
+
{entry}
|
|
231
|
+
tableParams={params}
|
|
232
|
+
/>
|
|
233
|
+
{/snippet}
|
|
234
|
+
{#snippet collapsible(entry)}
|
|
235
|
+
<ChildRecords
|
|
236
|
+
{collectionName}
|
|
237
|
+
recordId={entry.id}
|
|
238
|
+
width={dataTableWidth > dataTableContainerWidth
|
|
239
|
+
? dataTableContainerWidth
|
|
240
|
+
: dataTableWidth}
|
|
241
|
+
unifiedBgColor={unifiedBgColor ?? "bg-background"}
|
|
242
|
+
/>
|
|
243
|
+
{/snippet}
|
|
244
|
+
</Table>
|
|
245
|
+
{/if}
|
|
246
|
+
</div>
|
|
247
|
+
{/key}
|
|
242
248
|
</div>
|
|
243
249
|
{#if showFooter}
|
|
244
250
|
<Footer
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
import CreateManyButton from "../createManyButton.svelte";
|
|
11
11
|
import { showDialog } from "../confirmationDialog/store.svelte";
|
|
12
12
|
import CreateDetailViewButton from "../detailView/create/createDetailViewButton.svelte";
|
|
13
|
+
import ExtensionsComponents from "../extensionsComponents.svelte";
|
|
14
|
+
import { getExtensionUtils } from "../../extensions/extensionUtils";
|
|
13
15
|
import type { Snippet } from "svelte";
|
|
14
16
|
|
|
15
17
|
interface Props {
|
|
@@ -142,6 +144,12 @@
|
|
|
142
144
|
Icon={SquareStack}
|
|
143
145
|
onSuccessfullSave={() => (params = { ...params })}
|
|
144
146
|
></CreateManyButton>
|
|
147
|
+
<ExtensionsComponents
|
|
148
|
+
name="listView.header.actions"
|
|
149
|
+
utils={getExtensionUtils(lobb, ctx)}
|
|
150
|
+
{collectionName}
|
|
151
|
+
refresh={() => { params = { ...params }; }}
|
|
152
|
+
/>
|
|
145
153
|
<CreateDetailViewButton
|
|
146
154
|
{collectionName}
|
|
147
155
|
variant="default"
|
|
@@ -6,30 +6,27 @@
|
|
|
6
6
|
import Button from "./ui/button/button.svelte";
|
|
7
7
|
import { onMount } from "svelte";
|
|
8
8
|
import { getStudioContext } from "../context";
|
|
9
|
+
import { toast } from "svelte-sonner";
|
|
10
|
+
import Skeleton from "./ui/skeleton/skeleton.svelte";
|
|
9
11
|
|
|
10
12
|
const { lobb, ctx } = getStudioContext();
|
|
11
|
-
import { toast } from "svelte-sonner";
|
|
12
|
-
import Skeleton from "./ui/skeleton/skeleton.svelte";
|
|
13
13
|
|
|
14
14
|
interface Props {
|
|
15
15
|
collectionName: string;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
let {
|
|
19
|
-
collectionName,
|
|
20
|
-
}: Props = $props();
|
|
18
|
+
let { collectionName }: Props = $props();
|
|
21
19
|
|
|
22
20
|
let entry = $state({});
|
|
23
21
|
let loading = $state(true);
|
|
24
|
-
let singletonExists = $state(true);
|
|
25
22
|
const formFields = getCollectionFields(ctx, collectionName);
|
|
26
23
|
|
|
27
24
|
onMount(async () => {
|
|
28
|
-
const result = await lobb.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
25
|
+
const result = await fetch(`${lobb.lobbUrl}/api/collections/${collectionName}/singleton`, {
|
|
26
|
+
headers: lobb.getHeaders() as HeadersInit,
|
|
27
|
+
});
|
|
28
|
+
if (result.status !== 404) {
|
|
29
|
+
const json = await result.json();
|
|
33
30
|
entry = json.data;
|
|
34
31
|
}
|
|
35
32
|
loading = false;
|
|
@@ -43,29 +40,27 @@
|
|
|
43
40
|
}
|
|
44
41
|
</script>
|
|
45
42
|
|
|
46
|
-
<div>
|
|
47
|
-
<div class="flex justify-between items-center gap-2 p-2 border-b
|
|
43
|
+
<div class="flex flex-col h-full bg-background">
|
|
44
|
+
<div class="flex justify-between items-center gap-2 p-2 border-b h-10 shrink-0">
|
|
48
45
|
<div class="flex items-center gap-1">
|
|
49
46
|
<SidebarTrigger />
|
|
50
47
|
</div>
|
|
51
48
|
<div>
|
|
52
|
-
<Button
|
|
53
|
-
class="h-7 px-2 font-normal text-xs"
|
|
54
|
-
Icon={Save}
|
|
55
|
-
onclick={handleSave}
|
|
56
|
-
>
|
|
49
|
+
<Button class="h-7 px-2 font-normal text-xs" Icon={Save} onclick={handleSave}>
|
|
57
50
|
Save
|
|
58
51
|
</Button>
|
|
59
52
|
</div>
|
|
60
53
|
</div>
|
|
61
54
|
|
|
62
55
|
{#if loading}
|
|
63
|
-
<div class="flex flex-col gap-2 p-
|
|
56
|
+
<div class="flex flex-col gap-2 p-4 w-full">
|
|
64
57
|
<Skeleton class="h-8 w-full" />
|
|
65
58
|
<Skeleton class="h-8 w-[80%]" />
|
|
66
59
|
<Skeleton class="h-8 w-[60%]" />
|
|
67
60
|
</div>
|
|
68
61
|
{:else}
|
|
69
|
-
<
|
|
62
|
+
<div class="flex-1 overflow-y-auto max-w-xl">
|
|
63
|
+
<DetailViewForm bind:value={entry} fields={formFields} />
|
|
64
|
+
</div>
|
|
70
65
|
{/if}
|
|
71
66
|
</div>
|
|
@@ -93,7 +93,8 @@ export type ExtensionComponentKey =
|
|
|
93
93
|
| `detailView.fields.topRight.${string}.${string}`
|
|
94
94
|
| `detailView.fields.foreignKey.${string}`
|
|
95
95
|
| `listView.entry.children.${string}`
|
|
96
|
-
| `listView.entry.actions
|
|
96
|
+
| `listView.entry.actions`
|
|
97
|
+
| `listView.header.actions`;
|
|
97
98
|
|
|
98
99
|
export type ExtensionComponent =
|
|
99
100
|
| any
|