@lobb-js/studio 0.1.31
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/.env.example +1 -0
- package/.storybook/main.ts +31 -0
- package/.storybook/preview.ts +21 -0
- package/.storybook/vitest.setup.ts +7 -0
- package/README.md +47 -0
- package/components.json +16 -0
- package/docker-entrypoint.sh +7 -0
- package/dockerfile +27 -0
- package/index.html +13 -0
- package/package.json +77 -0
- package/public/lobb.svg +15 -0
- package/src/Studio.svelte +150 -0
- package/src/app.css +121 -0
- package/src/components-export.ts +21 -0
- package/src/extensions/extension.types.ts +93 -0
- package/src/extensions/extensionUtils.ts +192 -0
- package/src/lib/Lobb.ts +241 -0
- package/src/lib/components/LlmButton.svelte +136 -0
- package/src/lib/components/alertView.svelte +20 -0
- package/src/lib/components/breadCrumbs.svelte +60 -0
- package/src/lib/components/combobox.svelte +92 -0
- package/src/lib/components/confirmationDialog/confirmationDialog.svelte +33 -0
- package/src/lib/components/confirmationDialog/store.svelte.ts +28 -0
- package/src/lib/components/createManyButton.svelte +107 -0
- package/src/lib/components/dataTable/childRecords.svelte +140 -0
- package/src/lib/components/dataTable/dataTable.svelte +223 -0
- package/src/lib/components/dataTable/fieldCell.svelte +74 -0
- package/src/lib/components/dataTable/filter.svelte +282 -0
- package/src/lib/components/dataTable/filterButton.svelte +39 -0
- package/src/lib/components/dataTable/footer.svelte +84 -0
- package/src/lib/components/dataTable/header.svelte +154 -0
- package/src/lib/components/dataTable/sort.svelte +171 -0
- package/src/lib/components/dataTable/sortButton.svelte +36 -0
- package/src/lib/components/dataTable/table.svelte +337 -0
- package/src/lib/components/dataTable/utils.ts +127 -0
- package/src/lib/components/detailView/create/children.svelte +68 -0
- package/src/lib/components/detailView/create/createDetailView.svelte +226 -0
- package/src/lib/components/detailView/create/createDetailViewButton.svelte +32 -0
- package/src/lib/components/detailView/create/createManyView.svelte +250 -0
- package/src/lib/components/detailView/create/subRecords.svelte +48 -0
- package/src/lib/components/detailView/detailViewForm.svelte +104 -0
- package/src/lib/components/detailView/fieldCustomInput.svelte +23 -0
- package/src/lib/components/detailView/fieldInput.svelte +287 -0
- package/src/lib/components/detailView/fieldInputReplacement.svelte +199 -0
- package/src/lib/components/detailView/store.svelte.ts +61 -0
- package/src/lib/components/detailView/update/children.svelte +94 -0
- package/src/lib/components/detailView/update/updateDetailView.svelte +175 -0
- package/src/lib/components/detailView/update/updateDetailViewButton.svelte +32 -0
- package/src/lib/components/detailView/utils.ts +177 -0
- package/src/lib/components/diffViewer.svelte +102 -0
- package/src/lib/components/drawer.svelte +28 -0
- package/src/lib/components/extensionsComponents.svelte +31 -0
- package/src/lib/components/foreingKeyInput.svelte +80 -0
- package/src/lib/components/header.svelte +45 -0
- package/src/lib/components/loadingTypesForMonacoEditor.ts +36 -0
- package/src/lib/components/miniSidebar.svelte +238 -0
- package/src/lib/components/monacoEditor.svelte +181 -0
- package/src/lib/components/rangeCalendarButton.svelte +257 -0
- package/src/lib/components/selectRecord.svelte +126 -0
- package/src/lib/components/setServerPage.svelte +48 -0
- package/src/lib/components/sidebar/index.ts +4 -0
- package/src/lib/components/sidebar/sidebar.svelte +149 -0
- package/src/lib/components/sidebar/sidebarElements.svelte +144 -0
- package/src/lib/components/sidebar/sidebarTrigger.svelte +33 -0
- package/src/lib/components/singletone.svelte +69 -0
- package/src/lib/components/ui/accordion/accordion-content.svelte +22 -0
- package/src/lib/components/ui/accordion/accordion-item.svelte +12 -0
- package/src/lib/components/ui/accordion/accordion-trigger.svelte +31 -0
- package/src/lib/components/ui/accordion/index.ts +17 -0
- package/src/lib/components/ui/alert/alert-description.svelte +16 -0
- package/src/lib/components/ui/alert/alert-title.svelte +24 -0
- package/src/lib/components/ui/alert/alert.svelte +39 -0
- package/src/lib/components/ui/alert/index.ts +14 -0
- package/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte +13 -0
- package/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte +17 -0
- package/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte +26 -0
- package/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte +16 -0
- package/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte +20 -0
- package/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte +20 -0
- package/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte +19 -0
- package/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte +18 -0
- package/src/lib/components/ui/alert-dialog/index.ts +40 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb-ellipsis.svelte +23 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb-item.svelte +16 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb-link.svelte +31 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb-list.svelte +23 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb-page.svelte +23 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb-separator.svelte +27 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb.svelte +15 -0
- package/src/lib/components/ui/breadcrumb/index.ts +25 -0
- package/src/lib/components/ui/button/button.svelte +110 -0
- package/src/lib/components/ui/button/index.ts +17 -0
- package/src/lib/components/ui/checkbox/checkbox.svelte +35 -0
- package/src/lib/components/ui/checkbox/index.ts +6 -0
- package/src/lib/components/ui/command/command-dialog.svelte +35 -0
- package/src/lib/components/ui/command/command-empty.svelte +12 -0
- package/src/lib/components/ui/command/command-group.svelte +31 -0
- package/src/lib/components/ui/command/command-input.svelte +25 -0
- package/src/lib/components/ui/command/command-item.svelte +19 -0
- package/src/lib/components/ui/command/command-link-item.svelte +19 -0
- package/src/lib/components/ui/command/command-list.svelte +16 -0
- package/src/lib/components/ui/command/command-separator.svelte +12 -0
- package/src/lib/components/ui/command/command-shortcut.svelte +20 -0
- package/src/lib/components/ui/command/command.svelte +21 -0
- package/src/lib/components/ui/command/index.ts +40 -0
- package/src/lib/components/ui/dialog/dialog-content.svelte +38 -0
- package/src/lib/components/ui/dialog/dialog-description.svelte +16 -0
- package/src/lib/components/ui/dialog/dialog-footer.svelte +20 -0
- package/src/lib/components/ui/dialog/dialog-header.svelte +20 -0
- package/src/lib/components/ui/dialog/dialog-overlay.svelte +19 -0
- package/src/lib/components/ui/dialog/dialog-title.svelte +16 -0
- package/src/lib/components/ui/dialog/index.ts +37 -0
- package/src/lib/components/ui/input/index.ts +7 -0
- package/src/lib/components/ui/input/input.svelte +46 -0
- package/src/lib/components/ui/label/index.ts +7 -0
- package/src/lib/components/ui/label/label.svelte +19 -0
- package/src/lib/components/ui/popover/index.ts +17 -0
- package/src/lib/components/ui/popover/popover-content.svelte +28 -0
- package/src/lib/components/ui/range-calendar/index.ts +30 -0
- package/src/lib/components/ui/range-calendar/range-calendar-cell.svelte +19 -0
- package/src/lib/components/ui/range-calendar/range-calendar-day.svelte +35 -0
- package/src/lib/components/ui/range-calendar/range-calendar-grid-body.svelte +12 -0
- package/src/lib/components/ui/range-calendar/range-calendar-grid-head.svelte +12 -0
- package/src/lib/components/ui/range-calendar/range-calendar-grid-row.svelte +12 -0
- package/src/lib/components/ui/range-calendar/range-calendar-grid.svelte +16 -0
- package/src/lib/components/ui/range-calendar/range-calendar-head-cell.svelte +16 -0
- package/src/lib/components/ui/range-calendar/range-calendar-header.svelte +16 -0
- package/src/lib/components/ui/range-calendar/range-calendar-heading.svelte +16 -0
- package/src/lib/components/ui/range-calendar/range-calendar-months.svelte +20 -0
- package/src/lib/components/ui/range-calendar/range-calendar-next-button.svelte +27 -0
- package/src/lib/components/ui/range-calendar/range-calendar-prev-button.svelte +27 -0
- package/src/lib/components/ui/range-calendar/range-calendar.svelte +57 -0
- package/src/lib/components/ui/select/index.ts +34 -0
- package/src/lib/components/ui/select/select-content.svelte +38 -0
- package/src/lib/components/ui/select/select-group-heading.svelte +16 -0
- package/src/lib/components/ui/select/select-item.svelte +37 -0
- package/src/lib/components/ui/select/select-scroll-down-button.svelte +19 -0
- package/src/lib/components/ui/select/select-scroll-up-button.svelte +19 -0
- package/src/lib/components/ui/select/select-separator.svelte +13 -0
- package/src/lib/components/ui/select/select-trigger.svelte +24 -0
- package/src/lib/components/ui/separator/index.ts +7 -0
- package/src/lib/components/ui/separator/separator.svelte +22 -0
- package/src/lib/components/ui/skeleton/index.ts +7 -0
- package/src/lib/components/ui/skeleton/skeleton.svelte +22 -0
- package/src/lib/components/ui/sonner/index.ts +1 -0
- package/src/lib/components/ui/sonner/sonner.svelte +20 -0
- package/src/lib/components/ui/switch/index.ts +7 -0
- package/src/lib/components/ui/switch/switch.svelte +27 -0
- package/src/lib/components/ui/textarea/index.ts +7 -0
- package/src/lib/components/ui/textarea/textarea.svelte +22 -0
- package/src/lib/components/ui/tooltip/index.ts +18 -0
- package/src/lib/components/ui/tooltip/tooltip-content.svelte +21 -0
- package/src/lib/components/workflowEditor.svelte +187 -0
- package/src/lib/eventSystem.ts +38 -0
- package/src/lib/index.ts +40 -0
- package/src/lib/store.svelte.ts +21 -0
- package/src/lib/store.types.ts +28 -0
- package/src/lib/utils.ts +84 -0
- package/src/main.ts +18 -0
- package/src/routes/collections/collection.svelte +46 -0
- package/src/routes/collections/collections.svelte +43 -0
- package/src/routes/data_model/dataModel.svelte +40 -0
- package/src/routes/data_model/flow.css +22 -0
- package/src/routes/data_model/flow.svelte +82 -0
- package/src/routes/data_model/syncManager.svelte +93 -0
- package/src/routes/data_model/utils.ts +35 -0
- package/src/routes/extensions/extension.svelte +16 -0
- package/src/routes/home.svelte +36 -0
- package/src/routes/workflows/workflows.svelte +135 -0
- package/src/stories/Configure.mdx +364 -0
- package/src/stories/assets/accessibility.png +0 -0
- package/src/stories/assets/accessibility.svg +1 -0
- package/src/stories/assets/addon-library.png +0 -0
- package/src/stories/assets/assets.png +0 -0
- package/src/stories/assets/avif-test-image.avif +0 -0
- package/src/stories/assets/context.png +0 -0
- package/src/stories/assets/discord.svg +1 -0
- package/src/stories/assets/docs.png +0 -0
- package/src/stories/assets/figma-plugin.png +0 -0
- package/src/stories/assets/github.svg +1 -0
- package/src/stories/assets/share.png +0 -0
- package/src/stories/assets/styling.png +0 -0
- package/src/stories/assets/testing.png +0 -0
- package/src/stories/assets/theming.png +0 -0
- package/src/stories/assets/tutorials.svg +1 -0
- package/src/stories/assets/youtube.svg +1 -0
- package/src/stories/detailView/detailViewForm.stories.svelte +79 -0
- package/src/stories/examples/Button.stories.svelte +31 -0
- package/src/stories/examples/Button.svelte +30 -0
- package/src/stories/examples/Header.stories.svelte +26 -0
- package/src/stories/examples/Header.svelte +45 -0
- package/src/stories/examples/Page.stories.svelte +29 -0
- package/src/stories/examples/Page.svelte +70 -0
- package/src/stories/examples/button.css +30 -0
- package/src/stories/examples/header.css +32 -0
- package/src/stories/examples/page.css +68 -0
- package/src/vite-env.d.ts +2 -0
- package/svelte.config.js +7 -0
- package/todo.md +24 -0
- package/tsconfig.app.json +25 -0
- package/tsconfig.json +14 -0
- package/tsconfig.node.json +24 -0
- package/vite-plugin-contextual-lib.js +66 -0
- package/vite.build.svelte.config.ts +18 -0
- package/vite.config.ts +84 -0
- package/vite.extension.config.ts +81 -0
- package/vite_utils.ts +28 -0
- package/vitest.shims.d.ts +1 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import DataTable from "$lib/components/dataTable/dataTable.svelte";
|
|
3
|
+
import { ctx } from "$lib/store.svelte";
|
|
4
|
+
import { Link, Plus, TableIcon } from "lucide-svelte";
|
|
5
|
+
import CreateDetailViewButton from "../create/createDetailViewButton.svelte";
|
|
6
|
+
import ExtensionsComponents from "$lib/components/extensionsComponents.svelte";
|
|
7
|
+
import { getExtensionUtils } from "../../../../extensions/extensionUtils";
|
|
8
|
+
|
|
9
|
+
interface LocalProp {
|
|
10
|
+
collectionName: string;
|
|
11
|
+
entry: any;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let { collectionName, entry }: LocalProp = $props();
|
|
15
|
+
|
|
16
|
+
const childrenRelations = ctx.meta.relations.filter(
|
|
17
|
+
(relation) => relation.to.collection === collectionName,
|
|
18
|
+
);
|
|
19
|
+
const refresh: boolean[] = $state(
|
|
20
|
+
new Array(childrenRelations.length).fill(true),
|
|
21
|
+
);
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
{#if childrenRelations.length}
|
|
25
|
+
<div class="flex flex-col gap-4 border-t p-4">
|
|
26
|
+
<div class="flex items-center gap-2">
|
|
27
|
+
<Link size="17.5" />
|
|
28
|
+
<div>Sub Records</div>
|
|
29
|
+
</div>
|
|
30
|
+
<div class="flex flex-col gap-4">
|
|
31
|
+
{#each childrenRelations as relation, index}
|
|
32
|
+
{@const childCollection = relation.from.collection}
|
|
33
|
+
{@const childField = relation.from.field}
|
|
34
|
+
<ExtensionsComponents
|
|
35
|
+
name="detailView.update.subRecords.{childCollection}"
|
|
36
|
+
utils={getExtensionUtils()}
|
|
37
|
+
collectionName={childCollection}
|
|
38
|
+
filter={{
|
|
39
|
+
[childField]: entry.id,
|
|
40
|
+
}}
|
|
41
|
+
class="bg-soft border rounded-md overflow-hidden"
|
|
42
|
+
>
|
|
43
|
+
<div class="border rounded-md overflow-clip">
|
|
44
|
+
<div
|
|
45
|
+
class="flex items-center justify-between px-2 h-10 bg-soft border-b"
|
|
46
|
+
>
|
|
47
|
+
<div class="flex-1 flex h-full items-center gap-2">
|
|
48
|
+
<TableIcon
|
|
49
|
+
class="text-muted-foreground"
|
|
50
|
+
size="17.5"
|
|
51
|
+
/>
|
|
52
|
+
<div class="text-sm text-muted-foreground">
|
|
53
|
+
{childCollection}
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
<div class="flex gap-2">
|
|
57
|
+
<CreateDetailViewButton
|
|
58
|
+
variant="ghost"
|
|
59
|
+
class="h-7 px-2 font-normal text-xs"
|
|
60
|
+
Icon={Plus}
|
|
61
|
+
collectionName={childCollection}
|
|
62
|
+
onSuccessfullSave={async () => {
|
|
63
|
+
refresh[index] = !refresh[index];
|
|
64
|
+
}}
|
|
65
|
+
>
|
|
66
|
+
Create
|
|
67
|
+
</CreateDetailViewButton>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
<div class="max-h-72 overflow-auto rounded-md">
|
|
71
|
+
{#key refresh[index]}
|
|
72
|
+
<DataTable
|
|
73
|
+
collectionName={childCollection}
|
|
74
|
+
filter={{
|
|
75
|
+
[childField]: entry.id,
|
|
76
|
+
}}
|
|
77
|
+
unifiedBgColor="bg-soft"
|
|
78
|
+
showHeader={false}
|
|
79
|
+
showFooter={false}
|
|
80
|
+
showDelete={true}
|
|
81
|
+
tableProps={{
|
|
82
|
+
showLastColumnBorder: false,
|
|
83
|
+
showLastRowBorder: false,
|
|
84
|
+
showCheckboxes: false,
|
|
85
|
+
}}
|
|
86
|
+
/>
|
|
87
|
+
{/key}
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
</ExtensionsComponents>
|
|
91
|
+
{/each}
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
{/if}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
interface SubmitButton {
|
|
3
|
+
text: string;
|
|
4
|
+
icon: any;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface UpdateDetailViewProp {
|
|
8
|
+
collectionName: string;
|
|
9
|
+
recordId: string;
|
|
10
|
+
values?: Record<string, any>;
|
|
11
|
+
showRelatedRecords?: boolean;
|
|
12
|
+
rollback?: boolean;
|
|
13
|
+
submitButton?: SubmitButton;
|
|
14
|
+
title?: Snippet<[string]>;
|
|
15
|
+
onSuccessfullSave?: (entry: any) => Promise<void>;
|
|
16
|
+
onCancel?: () => Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<script lang="ts">
|
|
21
|
+
import { ArrowLeft, Pencil, X } from "lucide-svelte";
|
|
22
|
+
import Button from "$lib/components/ui/button/button.svelte";
|
|
23
|
+
import { fade, fly } from "svelte/transition";
|
|
24
|
+
import { lobb } from "$lib";
|
|
25
|
+
import { toast } from "svelte-sonner";
|
|
26
|
+
import { ctx } from "$lib/store.svelte";
|
|
27
|
+
import ExtensionsComponents from "../../extensionsComponents.svelte";
|
|
28
|
+
import { getExtensionUtils } from "../../../../extensions/extensionUtils";
|
|
29
|
+
import { calculateDrawerWidth, getChangedProperties } from "$lib/utils";
|
|
30
|
+
import { getField, getFieldIcon } from "../../dataTable/utils";
|
|
31
|
+
import Children from "../update/children.svelte";
|
|
32
|
+
import type { Snippet } from "svelte";
|
|
33
|
+
import { getDefaultEntry, parseDetailViewValues, serializeEntry } from "../utils";
|
|
34
|
+
import FieldInput from "../fieldInput.svelte";
|
|
35
|
+
import Drawer from "$lib/components/drawer.svelte";
|
|
36
|
+
|
|
37
|
+
let {
|
|
38
|
+
collectionName,
|
|
39
|
+
values = {},
|
|
40
|
+
showRelatedRecords = true,
|
|
41
|
+
onCancel,
|
|
42
|
+
onSuccessfullSave,
|
|
43
|
+
title,
|
|
44
|
+
submitButton,
|
|
45
|
+
recordId,
|
|
46
|
+
}: UpdateDetailViewProp = $props();
|
|
47
|
+
|
|
48
|
+
parseDetailViewValues(collectionName, values)
|
|
49
|
+
|
|
50
|
+
const fieldNames = Object.keys(ctx.meta.collections[collectionName].fields);
|
|
51
|
+
let entry: Record<string, any> = $state(
|
|
52
|
+
getDefaultEntry(fieldNames, collectionName, values),
|
|
53
|
+
);
|
|
54
|
+
const initialEntry = $state.snapshot(entry);
|
|
55
|
+
let localEntry = $derived(
|
|
56
|
+
getChangedProperties(initialEntry, $state.snapshot(entry)),
|
|
57
|
+
);
|
|
58
|
+
let fieldsErrors: Record<string, any> = $state({});
|
|
59
|
+
|
|
60
|
+
async function handleSave() {
|
|
61
|
+
delete localEntry.id;
|
|
62
|
+
localEntry = serializeEntry(collectionName, localEntry);
|
|
63
|
+
|
|
64
|
+
const response = await lobb.updateOne(
|
|
65
|
+
collectionName,
|
|
66
|
+
recordId,
|
|
67
|
+
localEntry,
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
if (!response.bodyUsed) {
|
|
71
|
+
const result = await response.json();
|
|
72
|
+
if (response.status >= 400) {
|
|
73
|
+
if (result.message && result.details) {
|
|
74
|
+
fieldsErrors = result.details;
|
|
75
|
+
return;
|
|
76
|
+
} else if (result.message) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// close detailView side bar
|
|
83
|
+
if (onSuccessfullSave) {
|
|
84
|
+
await onSuccessfullSave(localEntry);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
toast.success(`The record was successfully updated`);
|
|
88
|
+
|
|
89
|
+
onCancel?.();
|
|
90
|
+
}
|
|
91
|
+
</script>
|
|
92
|
+
|
|
93
|
+
<Drawer onHide={onCancel}>
|
|
94
|
+
<div class="flex h-12 items-center gap-4 border-b px-4">
|
|
95
|
+
<Button
|
|
96
|
+
variant="outline"
|
|
97
|
+
onclick={onCancel}
|
|
98
|
+
class=" h-8 w-8 rounded-full text-xs font-normal"
|
|
99
|
+
Icon={ArrowLeft}
|
|
100
|
+
></Button>
|
|
101
|
+
<div class="flex items-center gap-2 text-sm">
|
|
102
|
+
{#if title}
|
|
103
|
+
{@render title(collectionName)}
|
|
104
|
+
{:else}
|
|
105
|
+
<div>Update record of</div>
|
|
106
|
+
<div class="rounded-md border bg-muted px-2 py-0.5">
|
|
107
|
+
{collectionName}
|
|
108
|
+
</div>
|
|
109
|
+
{/if}
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
<div class="flex-1 overflow-y-auto">
|
|
113
|
+
<div class="flex flex-col gap-4 p-4">
|
|
114
|
+
{#each fieldNames as fieldName}
|
|
115
|
+
{@const field = getField(fieldName, collectionName)}
|
|
116
|
+
{@const FieldIcon = getFieldIcon(fieldName, collectionName)}
|
|
117
|
+
<div
|
|
118
|
+
class="flex flex-col gap-2"
|
|
119
|
+
>
|
|
120
|
+
<div
|
|
121
|
+
class="flex flex-1 items-end justify-between gap-2 text-xs"
|
|
122
|
+
>
|
|
123
|
+
<div class="flex gap-2">
|
|
124
|
+
<div class="h-fit">{field.label}</div>
|
|
125
|
+
<div
|
|
126
|
+
class="flex h-fit items-center gap-1 text-[0.7rem] text-muted-foreground"
|
|
127
|
+
>
|
|
128
|
+
<FieldIcon size="12" />
|
|
129
|
+
{field.type}
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
<div>
|
|
133
|
+
<ExtensionsComponents
|
|
134
|
+
name="dvFields.topRight.{collectionName}.{fieldName}"
|
|
135
|
+
utils={getExtensionUtils()}
|
|
136
|
+
bind:value={entry[fieldName]}
|
|
137
|
+
/>
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
<FieldInput
|
|
141
|
+
{collectionName}
|
|
142
|
+
{fieldName}
|
|
143
|
+
bind:value={entry[fieldName]}
|
|
144
|
+
{entry}
|
|
145
|
+
errorMessages={fieldsErrors[fieldName]}
|
|
146
|
+
/>
|
|
147
|
+
</div>
|
|
148
|
+
{/each}
|
|
149
|
+
</div>
|
|
150
|
+
{#if showRelatedRecords}
|
|
151
|
+
<Children {collectionName} {entry} />
|
|
152
|
+
{/if}
|
|
153
|
+
</div>
|
|
154
|
+
<div class="flex h-12 items-center justify-end gap-2 border-t px-4">
|
|
155
|
+
<div class="flex gap-3">
|
|
156
|
+
<Button
|
|
157
|
+
variant="outline"
|
|
158
|
+
onclick={onCancel}
|
|
159
|
+
class="h-7 px-3 text-xs font-normal"
|
|
160
|
+
Icon={X}
|
|
161
|
+
>
|
|
162
|
+
Cancel
|
|
163
|
+
</Button>
|
|
164
|
+
<Button
|
|
165
|
+
variant="default"
|
|
166
|
+
class="h-7 px-3 text-xs font-normal"
|
|
167
|
+
Icon={submitButton?.icon ? submitButton.icon : Pencil}
|
|
168
|
+
onclick={handleSave}
|
|
169
|
+
disabled={!Object.keys(localEntry).length}
|
|
170
|
+
>
|
|
171
|
+
{submitButton?.text ? submitButton.text : "Update"}
|
|
172
|
+
</Button>
|
|
173
|
+
</div>
|
|
174
|
+
</div>
|
|
175
|
+
</Drawer>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { UpdateDetailViewProp } from "./updateDetailView.svelte";
|
|
3
|
+
import type { ButtonProps } from "$lib/components/ui/button/button.svelte";
|
|
4
|
+
import Button from "$lib/components/ui/button/button.svelte";
|
|
5
|
+
import { openUpdateDetailView } from "../store.svelte";
|
|
6
|
+
|
|
7
|
+
interface LocalProp extends UpdateDetailViewProp {
|
|
8
|
+
variant?: ButtonProps["variant"];
|
|
9
|
+
class?: ButtonProps["class"];
|
|
10
|
+
Icon?: ButtonProps["Icon"];
|
|
11
|
+
children?: ButtonProps["children"];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let props: LocalProp = $props();
|
|
15
|
+
let entry: Record<string, any> | undefined = $state();
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<Button
|
|
19
|
+
variant={props.variant}
|
|
20
|
+
class={props.class}
|
|
21
|
+
Icon={props.Icon}
|
|
22
|
+
onclick={() => {
|
|
23
|
+
openUpdateDetailView({
|
|
24
|
+
values: entry,
|
|
25
|
+
...props
|
|
26
|
+
});
|
|
27
|
+
}}
|
|
28
|
+
>
|
|
29
|
+
{#if props.children}
|
|
30
|
+
{@render props.children()}
|
|
31
|
+
{/if}
|
|
32
|
+
</Button>
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import Mustache from "mustache";
|
|
2
|
+
import { ctx } from "$lib/store.svelte";
|
|
3
|
+
import { getFieldRelation } from "$lib/utils";
|
|
4
|
+
import { getField } from "../dataTable/utils";
|
|
5
|
+
import type { DetailFormField } from "./detailViewForm.svelte";
|
|
6
|
+
|
|
7
|
+
export function getDefaultEntry(fieldNames: string[], collectionName: string, values?: Record<string, any>) {
|
|
8
|
+
return Object.fromEntries(
|
|
9
|
+
fieldNames.map((fieldName) => {
|
|
10
|
+
let value = null;
|
|
11
|
+
const field = getField(fieldName, collectionName);
|
|
12
|
+
if (values && values[fieldName] !== undefined) {
|
|
13
|
+
value = values[fieldName];
|
|
14
|
+
} else if (field.pre_processors?.default) {
|
|
15
|
+
const defualtValue = field.pre_processors.default;
|
|
16
|
+
if (typeof defualtValue === "string") {
|
|
17
|
+
value = Mustache.render(defualtValue, {
|
|
18
|
+
now: new Date().toISOString(),
|
|
19
|
+
});
|
|
20
|
+
} else {
|
|
21
|
+
value = defualtValue;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return [fieldName, value];
|
|
25
|
+
}),
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function serializeEntry(
|
|
30
|
+
collectionName: string,
|
|
31
|
+
entry: Record<string, any>,
|
|
32
|
+
rollback: boolean = false,
|
|
33
|
+
) {
|
|
34
|
+
// deep clone the object
|
|
35
|
+
entry = { ...entry }
|
|
36
|
+
|
|
37
|
+
// serialize the foreign key field's value
|
|
38
|
+
for (const [fieldName, fieldValue] of Object.entries(entry)) {
|
|
39
|
+
const isRefrenceField = Boolean(getFieldRelation(collectionName, fieldName));
|
|
40
|
+
if (isRefrenceField && fieldValue !== null && fieldValue.id !== undefined) {
|
|
41
|
+
entry[fieldName] = fieldValue.id;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// check for related collections properties and serialize them too
|
|
46
|
+
if (!rollback) {
|
|
47
|
+
const childrenRelations = ctx.meta.relations.filter((relation) => relation.to.collection === collectionName);
|
|
48
|
+
const childrenCollectionNames = childrenRelations.map((relation) => relation.from.collection);
|
|
49
|
+
for (let index = 0; index < childrenCollectionNames.length; index++) {
|
|
50
|
+
const childrenCollectionName = childrenCollectionNames[index];
|
|
51
|
+
const childrenEntries = entry[childrenCollectionName];
|
|
52
|
+
if (childrenEntries) {
|
|
53
|
+
for (let index = 0; index < childrenEntries.length; index++) {
|
|
54
|
+
childrenEntries[index] = serializeEntry(childrenCollectionName, childrenEntries[index]);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return entry;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function generateTransactionBody(
|
|
64
|
+
collectionName: string,
|
|
65
|
+
entry: Record<string, any>,
|
|
66
|
+
) {
|
|
67
|
+
entry = { ...entry }
|
|
68
|
+
function handleEntryRecursive(
|
|
69
|
+
transactionBody: any[],
|
|
70
|
+
collectionName: string,
|
|
71
|
+
entry: Record<string, any>,
|
|
72
|
+
parentTransactionIndex?: number,
|
|
73
|
+
) {
|
|
74
|
+
const parentCollectionName = parentTransactionIndex !== undefined ? transactionBody[parentTransactionIndex].collection : null;
|
|
75
|
+
const foreignKeyFieldName = ctx.meta.relations.find(relation => relation.from.collection === collectionName && relation.to.collection === parentCollectionName)?.from.field;
|
|
76
|
+
const collectionFieldNames = Object.keys(ctx.meta.collections[collectionName].fields);
|
|
77
|
+
const payload: any = {};
|
|
78
|
+
for (let index = 0; index < collectionFieldNames.length; index++) {
|
|
79
|
+
const fieldName = collectionFieldNames[index];
|
|
80
|
+
const isForeignKeyField = fieldName === foreignKeyFieldName;
|
|
81
|
+
if (isForeignKeyField) {
|
|
82
|
+
payload[fieldName] = `{{ responses[${parentTransactionIndex}].data.id }}`
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
payload[fieldName] = entry[fieldName];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const localTransactionIndex = transactionBody.length;
|
|
90
|
+
if (payload.id) {
|
|
91
|
+
const localPayload = {
|
|
92
|
+
[foreignKeyFieldName]: payload[foreignKeyFieldName],
|
|
93
|
+
};
|
|
94
|
+
transactionBody.push({
|
|
95
|
+
collection: collectionName,
|
|
96
|
+
method: "updateMany",
|
|
97
|
+
args: [
|
|
98
|
+
localPayload,
|
|
99
|
+
{
|
|
100
|
+
id: payload.id
|
|
101
|
+
}
|
|
102
|
+
],
|
|
103
|
+
});
|
|
104
|
+
} else {
|
|
105
|
+
transactionBody.push({
|
|
106
|
+
collection: collectionName,
|
|
107
|
+
method: "createOne",
|
|
108
|
+
args: [payload],
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const childrenRelations = ctx.meta.relations.filter((relation) => relation.to.collection === collectionName);
|
|
113
|
+
const childrenCollectionNames = childrenRelations.map((relation) => relation.from.collection);
|
|
114
|
+
for (let index = 0; index < childrenCollectionNames.length; index++) {
|
|
115
|
+
const childrenCollectionName = childrenCollectionNames[index];
|
|
116
|
+
const childrenEntries = entry[childrenCollectionName];
|
|
117
|
+
if (childrenEntries) {
|
|
118
|
+
for (let index = 0; index < childrenEntries.length; index++) {
|
|
119
|
+
const childrenEntry = childrenEntries[index];
|
|
120
|
+
handleEntryRecursive(transactionBody, childrenCollectionName, childrenEntry, localTransactionIndex);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const transactionBody: any[] = [];
|
|
127
|
+
|
|
128
|
+
handleEntryRecursive(transactionBody, collectionName, entry);
|
|
129
|
+
|
|
130
|
+
return transactionBody
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function parseDetailViewValues(collectionName: string, values: Record<string, any>) {
|
|
134
|
+
const forignFieldNames = ctx.meta.relations
|
|
135
|
+
.filter((relation) => relation.from.collection === collectionName)
|
|
136
|
+
.map((relation) => relation.from.field);
|
|
137
|
+
const childCollectionNames = ctx.meta.relations
|
|
138
|
+
.filter((relation) => relation.to.collection === collectionName)
|
|
139
|
+
.map((relation) => relation.from.collection);
|
|
140
|
+
|
|
141
|
+
for (const [key, value] of Object.entries(values)) {
|
|
142
|
+
if (forignFieldNames.includes(key)) {
|
|
143
|
+
if (typeof value === 'number') {
|
|
144
|
+
values[key] = {
|
|
145
|
+
id: value,
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
} else if (childCollectionNames.includes(key)) {
|
|
149
|
+
for (let index = 0; index < values[key].length; index++) {
|
|
150
|
+
parseDetailViewValues(key, values[key][index]);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function getCollectionFields(collectionName: string) {
|
|
157
|
+
let returnedData: DetailFormField[] = [];
|
|
158
|
+
|
|
159
|
+
const collectionFields = ctx.meta.collections[collectionName].fields;
|
|
160
|
+
const isSingleton = ctx.meta.collections[collectionName].singleton;
|
|
161
|
+
for (const [fieldName, value] of Object.entries(collectionFields)) {
|
|
162
|
+
|
|
163
|
+
if (isSingleton && fieldName === "id") {
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
returnedData.push({
|
|
168
|
+
type: "input",
|
|
169
|
+
key: fieldName,
|
|
170
|
+
label: fieldName,
|
|
171
|
+
disabled: fieldName == "id",
|
|
172
|
+
placeholder: fieldName == "id" ? "AUTO_GENERATED" : "",
|
|
173
|
+
})
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return returnedData;
|
|
177
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onMount, onDestroy } from "svelte";
|
|
3
|
+
import * as monaco from "monaco-editor";
|
|
4
|
+
import editorWorker from "monaco-editor/esm/vs/editor/editor.worker?worker";
|
|
5
|
+
import jsonWorker from "monaco-editor/esm/vs/language/json/json.worker?worker";
|
|
6
|
+
import cssWorker from "monaco-editor/esm/vs/language/css/css.worker?worker";
|
|
7
|
+
import htmlWorker from "monaco-editor/esm/vs/language/html/html.worker?worker";
|
|
8
|
+
import tsWorker from "monaco-editor/esm/vs/language/typescript/ts.worker?worker";
|
|
9
|
+
import { cn } from "$lib/utils";
|
|
10
|
+
|
|
11
|
+
interface Props {
|
|
12
|
+
type: "javascript" | "typescript" | "json";
|
|
13
|
+
original: string;
|
|
14
|
+
modified: string;
|
|
15
|
+
class?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let {
|
|
19
|
+
type,
|
|
20
|
+
original,
|
|
21
|
+
modified,
|
|
22
|
+
class: className,
|
|
23
|
+
...props
|
|
24
|
+
}: Props = $props();
|
|
25
|
+
|
|
26
|
+
let editorContainer: HTMLDivElement;
|
|
27
|
+
let editor: monaco.editor.IStandaloneDiffEditor;
|
|
28
|
+
let originalModel: monaco.editor.ITextModel;
|
|
29
|
+
let modifiedModel: monaco.editor.ITextModel;
|
|
30
|
+
|
|
31
|
+
self.MonacoEnvironment = {
|
|
32
|
+
getWorker(_, label) {
|
|
33
|
+
if (label === "json") {
|
|
34
|
+
return new jsonWorker();
|
|
35
|
+
}
|
|
36
|
+
if (label === "css" || label === "scss" || label === "less") {
|
|
37
|
+
return new cssWorker();
|
|
38
|
+
}
|
|
39
|
+
if (
|
|
40
|
+
label === "html" ||
|
|
41
|
+
label === "handlebars" ||
|
|
42
|
+
label === "razor"
|
|
43
|
+
) {
|
|
44
|
+
return new htmlWorker();
|
|
45
|
+
}
|
|
46
|
+
if (label === "typescript" || label === "javascript") {
|
|
47
|
+
return new tsWorker();
|
|
48
|
+
}
|
|
49
|
+
return new editorWorker();
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
onMount(async () => {
|
|
54
|
+
monaco.editor.defineTheme("transparentTheme", {
|
|
55
|
+
base: "vs",
|
|
56
|
+
inherit: true,
|
|
57
|
+
rules: [],
|
|
58
|
+
colors: {
|
|
59
|
+
"editor.background": "#EFEFEF00",
|
|
60
|
+
focusBorder: "#00000000",
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
originalModel = monaco.editor.createModel(original, type);
|
|
65
|
+
|
|
66
|
+
modifiedModel = monaco.editor.createModel(modified, type);
|
|
67
|
+
|
|
68
|
+
editor = monaco.editor.createDiffEditor(editorContainer, {
|
|
69
|
+
renderSideBySide: true,
|
|
70
|
+
readOnly: true,
|
|
71
|
+
automaticLayout: true,
|
|
72
|
+
theme: "transparentTheme",
|
|
73
|
+
minimap: { enabled: false },
|
|
74
|
+
glyphMargin: false,
|
|
75
|
+
overviewRulerLanes: 0,
|
|
76
|
+
hideCursorInOverviewRuler: true,
|
|
77
|
+
stickyScroll: { enabled: false },
|
|
78
|
+
lineNumbersMinChars: 2,
|
|
79
|
+
scrollBeyondLastLine: false,
|
|
80
|
+
padding: { top: 10, bottom: 1 },
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
editor.setModel({
|
|
84
|
+
original: originalModel,
|
|
85
|
+
modified: modifiedModel,
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
onDestroy(() => {
|
|
90
|
+
editor?.dispose();
|
|
91
|
+
originalModel.dispose();
|
|
92
|
+
modifiedModel.dispose();
|
|
93
|
+
});
|
|
94
|
+
</script>
|
|
95
|
+
|
|
96
|
+
<div class={cn("resize-y rounded-md border bg-soft shadow-sm h-60", className)}>
|
|
97
|
+
<div
|
|
98
|
+
bind:this={editorContainer}
|
|
99
|
+
class="editor pl-2"
|
|
100
|
+
style="height: 100%; width: 100%;"
|
|
101
|
+
></div>
|
|
102
|
+
</div>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { calculateDrawerWidth } from "$lib/utils";
|
|
3
|
+
import type { Snippet } from "svelte";
|
|
4
|
+
import { fade, fly } from "svelte/transition";
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
children?: Snippet<[]>;
|
|
8
|
+
onHide?: () => Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let { onHide, children }: Props = $props();
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<button
|
|
15
|
+
transition:fade={{ duration: 250 }}
|
|
16
|
+
onclick={() => onHide?.()}
|
|
17
|
+
class="backgroundDrawerButton fixed left-0 top-0 z-30 h-screen w-screen bg-background opacity-50 cursor-default"
|
|
18
|
+
aria-label="background used to hide the background"
|
|
19
|
+
></button>
|
|
20
|
+
|
|
21
|
+
<!-- the drawer -->
|
|
22
|
+
<div
|
|
23
|
+
transition:fly={{ x: "100%", duration: 250 }}
|
|
24
|
+
class="fixed right-0 top-0 z-30 flex h-full w-full flex-col border-l bg-background"
|
|
25
|
+
style="max-width: {calculateDrawerWidth()}px;"
|
|
26
|
+
>
|
|
27
|
+
{@render children?.()}
|
|
28
|
+
</div>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
import { loadExtensionComponents } from "../../extensions/extensionUtils";
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
name: string;
|
|
7
|
+
filterByExtensions?: string[];
|
|
8
|
+
value?: unknown;
|
|
9
|
+
children?: Snippet<[]>;
|
|
10
|
+
[key: string]: any;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let {
|
|
14
|
+
name,
|
|
15
|
+
filterByExtensions,
|
|
16
|
+
value = $bindable(),
|
|
17
|
+
children,
|
|
18
|
+
...props
|
|
19
|
+
}: Props = $props();
|
|
20
|
+
|
|
21
|
+
const Components = loadExtensionComponents(name, filterByExtensions);
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
{#if Components.length}
|
|
25
|
+
{#each Components as Component}
|
|
26
|
+
<Component bind:value {...props} />
|
|
27
|
+
{/each}
|
|
28
|
+
{:else}
|
|
29
|
+
{@render children?.()}
|
|
30
|
+
{/if}
|
|
31
|
+
|