@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,68 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { ctx } from "$lib/store.svelte";
|
|
3
|
+
import { Link } from "lucide-svelte";
|
|
4
|
+
import CreateManyView from "./createManyView.svelte";
|
|
5
|
+
import ExtensionsComponents from "$lib/components/extensionsComponents.svelte";
|
|
6
|
+
import { getExtensionUtils } from "../../../../extensions/extensionUtils";
|
|
7
|
+
|
|
8
|
+
interface LocalProp {
|
|
9
|
+
collectionName: string;
|
|
10
|
+
entry: any;
|
|
11
|
+
values?: Record<string, any>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let { collectionName, entry = $bindable(), values = {} }: LocalProp = $props();
|
|
15
|
+
|
|
16
|
+
const childrenRelations = ctx.meta.relations.filter(
|
|
17
|
+
(relation) => relation.to.collection === collectionName,
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
// filling the children properties
|
|
21
|
+
for (let index = 0; index < childrenRelations.length; index++) {
|
|
22
|
+
const relation = childrenRelations[index];
|
|
23
|
+
const childCollection = relation.from.collection;
|
|
24
|
+
entry[childCollection] = [];
|
|
25
|
+
if (values[childCollection]) {
|
|
26
|
+
entry[childCollection] = [
|
|
27
|
+
...entry[childCollection],
|
|
28
|
+
...values[childCollection],
|
|
29
|
+
];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
{#if childrenRelations.length}
|
|
35
|
+
<div class="flex flex-col gap-4 border-t p-4">
|
|
36
|
+
<div class="flex items-center gap-2">
|
|
37
|
+
<Link size="17.5" />
|
|
38
|
+
<div>Sub Records</div>
|
|
39
|
+
</div>
|
|
40
|
+
<div class="flex flex-col gap-4">
|
|
41
|
+
{#each childrenRelations as relation}
|
|
42
|
+
{@const childCollection = relation.from.collection}
|
|
43
|
+
<ExtensionsComponents
|
|
44
|
+
name="detailView.create.subRecords.{childCollection}"
|
|
45
|
+
utils={getExtensionUtils()}
|
|
46
|
+
parentCollectionName={collectionName}
|
|
47
|
+
collectionName={childCollection}
|
|
48
|
+
parentRecord={{
|
|
49
|
+
id: entry.id,
|
|
50
|
+
collectionName: collectionName,
|
|
51
|
+
}}
|
|
52
|
+
class="bg-soft border rounded-md overflow-hidden"
|
|
53
|
+
bind:value={entry[childCollection]}
|
|
54
|
+
>
|
|
55
|
+
<CreateManyView
|
|
56
|
+
parentCollectionName={collectionName}
|
|
57
|
+
collectionName={childCollection}
|
|
58
|
+
parentRecord={{
|
|
59
|
+
id: entry.id,
|
|
60
|
+
collectionName: collectionName,
|
|
61
|
+
}}
|
|
62
|
+
bind:entries={entry[childCollection]}
|
|
63
|
+
/>
|
|
64
|
+
</ExtensionsComponents>
|
|
65
|
+
{/each}
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
{/if}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
interface SubmitButton {
|
|
3
|
+
text: string;
|
|
4
|
+
icon: any;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface CreateDetailViewProp {
|
|
8
|
+
collectionName: string;
|
|
9
|
+
values?: Record<string, any>;
|
|
10
|
+
showRelatedRecords?: boolean;
|
|
11
|
+
rollback?: boolean;
|
|
12
|
+
submitButton?: SubmitButton;
|
|
13
|
+
title?: Snippet<[string]>;
|
|
14
|
+
onSuccessfullSave?: (entry: any) => Promise<void>;
|
|
15
|
+
onCancel?: () => Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<script lang="ts">
|
|
20
|
+
import { ArrowLeft, Plus, X } from "lucide-svelte";
|
|
21
|
+
import Button from "$lib/components/ui/button/button.svelte";
|
|
22
|
+
import { lobb } from "$lib";
|
|
23
|
+
import { toast } from "svelte-sonner";
|
|
24
|
+
import { ctx } from "$lib/store.svelte";
|
|
25
|
+
import ExtensionsComponents from "../../extensionsComponents.svelte";
|
|
26
|
+
import { getExtensionUtils } from "../../../../extensions/extensionUtils";
|
|
27
|
+
import { getField, getFieldIcon } from "../../dataTable/utils";
|
|
28
|
+
import Children from "./children.svelte";
|
|
29
|
+
import {
|
|
30
|
+
generateTransactionBody,
|
|
31
|
+
getDefaultEntry,
|
|
32
|
+
parseDetailViewValues,
|
|
33
|
+
serializeEntry,
|
|
34
|
+
} from "../utils";
|
|
35
|
+
import type { Snippet } from "svelte";
|
|
36
|
+
import FieldInput from "../fieldInput.svelte";
|
|
37
|
+
import { emitEvent } from "$lib/eventSystem";
|
|
38
|
+
import Drawer from "$lib/components/drawer.svelte";
|
|
39
|
+
|
|
40
|
+
let {
|
|
41
|
+
collectionName,
|
|
42
|
+
values = {},
|
|
43
|
+
showRelatedRecords = true,
|
|
44
|
+
rollback = false,
|
|
45
|
+
onCancel,
|
|
46
|
+
onSuccessfullSave,
|
|
47
|
+
title,
|
|
48
|
+
submitButton,
|
|
49
|
+
}: CreateDetailViewProp = $props();
|
|
50
|
+
|
|
51
|
+
parseDetailViewValues(collectionName, values)
|
|
52
|
+
|
|
53
|
+
const fieldNames = Object.keys(ctx.meta.collections[collectionName].fields);
|
|
54
|
+
let entry: Record<string, any> = $state(
|
|
55
|
+
getDefaultEntry(fieldNames, collectionName, values),
|
|
56
|
+
);
|
|
57
|
+
let fieldsErrors: Record<string, any> = $state({});
|
|
58
|
+
const subCollections = ctx.meta.relations.filter((relation) => relation.to.collection === collectionName).map((relation) => relation.from.collection);
|
|
59
|
+
const subCollectionsValues: Record<string, any> = {};
|
|
60
|
+
for (let index = 0; index < subCollections.length; index++) {
|
|
61
|
+
const subCollection = subCollections[index];
|
|
62
|
+
if (values[subCollection]) {
|
|
63
|
+
subCollectionsValues[subCollection] = values[subCollection];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function handleSave() {
|
|
68
|
+
let localEntry = $state.snapshot(entry);
|
|
69
|
+
|
|
70
|
+
await emitEvent("studio.collections.preCreate", {
|
|
71
|
+
collectionName,
|
|
72
|
+
entry: localEntry,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// remove empty children records data
|
|
76
|
+
for (const [key, value] of Object.entries(localEntry)) {
|
|
77
|
+
if (Array.isArray(value) && !value.length) {
|
|
78
|
+
delete localEntry[key];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const serializedEntry = serializeEntry(
|
|
83
|
+
collectionName,
|
|
84
|
+
localEntry,
|
|
85
|
+
rollback,
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
let transactionBody;
|
|
89
|
+
if (rollback) {
|
|
90
|
+
transactionBody = [
|
|
91
|
+
{
|
|
92
|
+
collection: collectionName,
|
|
93
|
+
method: "createOne",
|
|
94
|
+
args: [serializedEntry],
|
|
95
|
+
},
|
|
96
|
+
];
|
|
97
|
+
} else {
|
|
98
|
+
transactionBody = generateTransactionBody(
|
|
99
|
+
collectionName,
|
|
100
|
+
serializedEntry,
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// create the record
|
|
105
|
+
let response = await lobb.transactions(transactionBody, rollback);
|
|
106
|
+
|
|
107
|
+
await emitEvent("studio.collections.create", {
|
|
108
|
+
collectionName,
|
|
109
|
+
entry: localEntry,
|
|
110
|
+
response: response,
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
if (!response.bodyUsed) {
|
|
114
|
+
let result = await response.json();
|
|
115
|
+
if (response.status >= 400) {
|
|
116
|
+
if (result.message && result.details) {
|
|
117
|
+
fieldsErrors = result.details;
|
|
118
|
+
return;
|
|
119
|
+
} else if (result.message) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// close detailView side bar
|
|
126
|
+
if (onSuccessfullSave) {
|
|
127
|
+
await onSuccessfullSave(localEntry);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (!rollback) {
|
|
131
|
+
toast.success(`The record was successfully created`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
onCancel?.();
|
|
135
|
+
}
|
|
136
|
+
</script>
|
|
137
|
+
|
|
138
|
+
<Drawer onHide={onCancel}>
|
|
139
|
+
<div class="flex h-12 items-center gap-4 border-b px-4">
|
|
140
|
+
<Button
|
|
141
|
+
variant="outline"
|
|
142
|
+
onclick={onCancel}
|
|
143
|
+
class=" h-8 w-8 rounded-full text-xs font-normal"
|
|
144
|
+
Icon={ArrowLeft}
|
|
145
|
+
></Button>
|
|
146
|
+
<div class="flex items-center gap-2 text-sm">
|
|
147
|
+
{#if title}
|
|
148
|
+
{@render title(collectionName)}
|
|
149
|
+
{:else}
|
|
150
|
+
<div>Create new record to</div>
|
|
151
|
+
<div class="rounded-md border bg-muted px-2 py-0.5">
|
|
152
|
+
{collectionName}
|
|
153
|
+
</div>
|
|
154
|
+
{/if}
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
<div class="flex-1 overflow-y-auto">
|
|
158
|
+
<div class="flex flex-col gap-4 p-4">
|
|
159
|
+
{#each fieldNames as fieldName}
|
|
160
|
+
{@const field = getField(fieldName, collectionName)}
|
|
161
|
+
{@const FieldIcon = getFieldIcon(fieldName, collectionName)}
|
|
162
|
+
<div
|
|
163
|
+
class="flex flex-col gap-2"
|
|
164
|
+
>
|
|
165
|
+
<div
|
|
166
|
+
class="flex flex-1 items-end justify-between gap-2 text-xs"
|
|
167
|
+
>
|
|
168
|
+
<div class="flex gap-2">
|
|
169
|
+
<div class="h-fit">{field.label}</div>
|
|
170
|
+
<div
|
|
171
|
+
class="flex h-fit items-center gap-1 text-[0.7rem] text-muted-foreground"
|
|
172
|
+
>
|
|
173
|
+
<FieldIcon size="12" />
|
|
174
|
+
{field.type}
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
<div>
|
|
178
|
+
<ExtensionsComponents
|
|
179
|
+
name="dvFields.topRight.{collectionName}.{fieldName}"
|
|
180
|
+
utils={getExtensionUtils()}
|
|
181
|
+
bind:value={entry[fieldName]}
|
|
182
|
+
/>
|
|
183
|
+
</div>
|
|
184
|
+
</div>
|
|
185
|
+
<FieldInput
|
|
186
|
+
{collectionName}
|
|
187
|
+
{fieldName}
|
|
188
|
+
bind:value={
|
|
189
|
+
() => entry[fieldName],
|
|
190
|
+
(v) =>
|
|
191
|
+
(entry = {
|
|
192
|
+
...entry,
|
|
193
|
+
[fieldName]: v,
|
|
194
|
+
})
|
|
195
|
+
}
|
|
196
|
+
{entry}
|
|
197
|
+
errorMessages={fieldsErrors[fieldName]}
|
|
198
|
+
/>
|
|
199
|
+
</div>
|
|
200
|
+
{/each}
|
|
201
|
+
</div>
|
|
202
|
+
{#if showRelatedRecords}
|
|
203
|
+
<Children {collectionName} values={subCollectionsValues} bind:entry />
|
|
204
|
+
{/if}
|
|
205
|
+
</div>
|
|
206
|
+
<div class="flex h-12 items-center justify-end gap-2 border-t px-4">
|
|
207
|
+
<div class="flex gap-3">
|
|
208
|
+
<Button
|
|
209
|
+
variant="outline"
|
|
210
|
+
onclick={onCancel}
|
|
211
|
+
class="h-7 px-3 text-xs font-normal"
|
|
212
|
+
Icon={X}
|
|
213
|
+
>
|
|
214
|
+
Cancel
|
|
215
|
+
</Button>
|
|
216
|
+
<Button
|
|
217
|
+
variant="default"
|
|
218
|
+
class="h-7 px-3 text-xs font-normal"
|
|
219
|
+
Icon={submitButton?.icon ? submitButton.icon : Plus}
|
|
220
|
+
onclick={handleSave}
|
|
221
|
+
>
|
|
222
|
+
{submitButton?.text ? submitButton.text : "Create"}
|
|
223
|
+
</Button>
|
|
224
|
+
</div>
|
|
225
|
+
</div>
|
|
226
|
+
</Drawer>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { CreateDetailViewProp } from "./createDetailView.svelte";
|
|
3
|
+
import type { ButtonProps } from "$lib/components/ui/button/button.svelte";
|
|
4
|
+
import Button from "$lib/components/ui/button/button.svelte";
|
|
5
|
+
import { openCreateDetailView } from "../store.svelte";
|
|
6
|
+
|
|
7
|
+
interface LocalProp extends CreateDetailViewProp {
|
|
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
|
+
openCreateDetailView({
|
|
24
|
+
values: entry,
|
|
25
|
+
...props
|
|
26
|
+
});
|
|
27
|
+
}}
|
|
28
|
+
>
|
|
29
|
+
{#if props.children}
|
|
30
|
+
{@render props.children()}
|
|
31
|
+
{/if}
|
|
32
|
+
</Button>
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
3
|
+
import {
|
|
4
|
+
ChevronRight,
|
|
5
|
+
Pencil,
|
|
6
|
+
Plus,
|
|
7
|
+
Replace,
|
|
8
|
+
Table as TableIcon,
|
|
9
|
+
X,
|
|
10
|
+
} from "lucide-svelte";
|
|
11
|
+
import Table, { type TableProps } from "../../dataTable/table.svelte";
|
|
12
|
+
import Button from "../../ui/button/button.svelte";
|
|
13
|
+
import CreateDetailViewButton from "./createDetailViewButton.svelte";
|
|
14
|
+
import { getCollectionColumns } from "$lib/components/dataTable/utils";
|
|
15
|
+
import SelectRecord from "$lib/components/selectRecord.svelte";
|
|
16
|
+
import FieldCell from "$lib/components/dataTable/fieldCell.svelte";
|
|
17
|
+
import SubRecords from "./subRecords.svelte";
|
|
18
|
+
import ChildRecords from "$lib/components/dataTable/childRecords.svelte";
|
|
19
|
+
import { ctx } from "$lib/store.svelte";
|
|
20
|
+
|
|
21
|
+
interface ParentRecord {
|
|
22
|
+
id: string;
|
|
23
|
+
collectionName: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface LocalProp {
|
|
27
|
+
parentCollectionName: string;
|
|
28
|
+
collectionName: string;
|
|
29
|
+
entries: any[];
|
|
30
|
+
parentRecord?: ParentRecord;
|
|
31
|
+
class?: HTMLAttributes<HTMLDivElement>["class"];
|
|
32
|
+
expanded?: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let {
|
|
36
|
+
parentCollectionName,
|
|
37
|
+
collectionName,
|
|
38
|
+
entries = $bindable(),
|
|
39
|
+
class: className,
|
|
40
|
+
expanded = true,
|
|
41
|
+
parentRecord,
|
|
42
|
+
}: LocalProp = $props();
|
|
43
|
+
|
|
44
|
+
let tableWidth: number = $state(0);
|
|
45
|
+
const doesCollectionHasChildren = Boolean(
|
|
46
|
+
ctx.meta.relations.find(
|
|
47
|
+
(relation) => relation.to.collection === collectionName,
|
|
48
|
+
),
|
|
49
|
+
);
|
|
50
|
+
const columns: TableProps["columns"] = getCollectionColumns(collectionName);
|
|
51
|
+
const refrenceFieldName = ctx.meta.relations.find(
|
|
52
|
+
(relation) =>
|
|
53
|
+
relation.from.collection === collectionName &&
|
|
54
|
+
relation.to.collection === parentRecord?.collectionName,
|
|
55
|
+
)?.from.field;
|
|
56
|
+
const createValues = {
|
|
57
|
+
[refrenceFieldName]: {
|
|
58
|
+
id: 0,
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
let selectedRecordsIds: string[] = $derived(
|
|
62
|
+
entries.filter((entry) => entry.id).map((entry) => entry.id),
|
|
63
|
+
);
|
|
64
|
+
let selectRecordFilter: Record<string, any> = $state({});
|
|
65
|
+
|
|
66
|
+
$effect(() => {
|
|
67
|
+
if (selectedRecordsIds.length) {
|
|
68
|
+
selectRecordFilter = {
|
|
69
|
+
id: {
|
|
70
|
+
$nin: selectedRecordsIds,
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
} else {
|
|
74
|
+
selectRecordFilter = {};
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
async function onRecordAdd(entry: any) {
|
|
79
|
+
delete entry.id;
|
|
80
|
+
entries.unshift(entry);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function onRecordOverride(entry: any, index: number) {
|
|
84
|
+
delete entry.id;
|
|
85
|
+
entries[index] = entry;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function onRecordSelect(entry: any) {
|
|
89
|
+
entries.unshift(entry);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function onRecordSelectReplace(entry: any, index: number) {
|
|
93
|
+
entries[index] = entry;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function onRecordRemove(entry: any, index: number) {
|
|
97
|
+
entries.splice(index, 1);
|
|
98
|
+
}
|
|
99
|
+
</script>
|
|
100
|
+
|
|
101
|
+
<div
|
|
102
|
+
class="
|
|
103
|
+
flex flex-col border rounded-md overflow-clip
|
|
104
|
+
{className ? className : ''}
|
|
105
|
+
"
|
|
106
|
+
>
|
|
107
|
+
<div
|
|
108
|
+
class="
|
|
109
|
+
flex items-center justify-between px-2 h-10 bg-soft
|
|
110
|
+
{expanded ? 'border-b' : ''}
|
|
111
|
+
"
|
|
112
|
+
>
|
|
113
|
+
<button
|
|
114
|
+
onclick={() => (expanded = !expanded)}
|
|
115
|
+
class="flex-1 flex h-full items-center gap-2"
|
|
116
|
+
>
|
|
117
|
+
<ChevronRight
|
|
118
|
+
class="text-muted-foreground transition-transform"
|
|
119
|
+
style={expanded
|
|
120
|
+
? "transform: rotate(90deg);"
|
|
121
|
+
: "transform: rotate(0deg);"}
|
|
122
|
+
size="17.5"
|
|
123
|
+
/>
|
|
124
|
+
<TableIcon class="text-muted-foreground" size="17.5" />
|
|
125
|
+
<div class="text-sm text-muted-foreground">{collectionName}</div>
|
|
126
|
+
</button>
|
|
127
|
+
<div class="flex gap-2">
|
|
128
|
+
<SelectRecord
|
|
129
|
+
{parentCollectionName}
|
|
130
|
+
{collectionName}
|
|
131
|
+
text="Select existing"
|
|
132
|
+
onSelect={onRecordSelect}
|
|
133
|
+
filter={selectRecordFilter}
|
|
134
|
+
class="h-7 px-2 font-normal text-xs"
|
|
135
|
+
variant="ghost"
|
|
136
|
+
/>
|
|
137
|
+
<CreateDetailViewButton
|
|
138
|
+
variant="ghost"
|
|
139
|
+
class="h-7 px-2 font-normal text-xs"
|
|
140
|
+
Icon={Plus}
|
|
141
|
+
{collectionName}
|
|
142
|
+
rollback={true}
|
|
143
|
+
showRelatedRecords={true}
|
|
144
|
+
onSuccessfullSave={onRecordAdd}
|
|
145
|
+
values={createValues}
|
|
146
|
+
submitButton={{
|
|
147
|
+
text: "Add",
|
|
148
|
+
icon: Plus,
|
|
149
|
+
}}
|
|
150
|
+
>
|
|
151
|
+
{#snippet title(collectionName)}
|
|
152
|
+
<div>Add record to</div>
|
|
153
|
+
<div class="rounded-md border bg-muted px-2 py-0.5">
|
|
154
|
+
{collectionName}
|
|
155
|
+
</div>
|
|
156
|
+
{/snippet}
|
|
157
|
+
Add
|
|
158
|
+
</CreateDetailViewButton>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
{#if expanded}
|
|
162
|
+
<div bind:clientWidth={tableWidth} class="bg-soft overflow-auto">
|
|
163
|
+
<Table
|
|
164
|
+
data={entries}
|
|
165
|
+
{columns}
|
|
166
|
+
selectByColumn="id"
|
|
167
|
+
showCollapsible={doesCollectionHasChildren}
|
|
168
|
+
unifiedBgColor="bg-soft"
|
|
169
|
+
>
|
|
170
|
+
{#snippet tools(entry, index)}
|
|
171
|
+
<Button
|
|
172
|
+
class="h-6 w-6 text-muted-foreground hover:bg-transparent"
|
|
173
|
+
variant="ghost"
|
|
174
|
+
size="icon"
|
|
175
|
+
onclick={() => onRecordRemove(entry, index)}
|
|
176
|
+
Icon={X}
|
|
177
|
+
></Button>
|
|
178
|
+
{#if entry.id}
|
|
179
|
+
<!-- TODO: remove this. I think its totally unneccessary.
|
|
180
|
+
its the replace record button. there is no need for that.
|
|
181
|
+
that can just delete and add a new one -->
|
|
182
|
+
<!-- <SelectRecord
|
|
183
|
+
{collectionName}
|
|
184
|
+
onSelect={(entry) =>
|
|
185
|
+
onRecordSelectReplace(entry, index)}
|
|
186
|
+
filter={selectRecordFilter}
|
|
187
|
+
class="h-6 w-6 text-muted-foreground hover:bg-transparent p-0"
|
|
188
|
+
variant="ghost"
|
|
189
|
+
>
|
|
190
|
+
<Replace size="17.5" />
|
|
191
|
+
</SelectRecord> -->
|
|
192
|
+
{:else}
|
|
193
|
+
<CreateDetailViewButton
|
|
194
|
+
variant="ghost"
|
|
195
|
+
class="h-6 w-6 text-muted-foreground hover:bg-transparent p-0"
|
|
196
|
+
Icon={Pencil}
|
|
197
|
+
{collectionName}
|
|
198
|
+
rollback={true}
|
|
199
|
+
showRelatedRecords={true}
|
|
200
|
+
onSuccessfullSave={(entry) =>
|
|
201
|
+
onRecordOverride(entry, index)}
|
|
202
|
+
values={entry}
|
|
203
|
+
submitButton={{
|
|
204
|
+
text: "Edit",
|
|
205
|
+
icon: Pencil,
|
|
206
|
+
}}
|
|
207
|
+
>
|
|
208
|
+
{#snippet title(collectionName)}
|
|
209
|
+
<div>Update record of</div>
|
|
210
|
+
<div
|
|
211
|
+
class="rounded-md border bg-muted px-2 py-0.5"
|
|
212
|
+
>
|
|
213
|
+
{collectionName}
|
|
214
|
+
</div>
|
|
215
|
+
{/snippet}
|
|
216
|
+
</CreateDetailViewButton>
|
|
217
|
+
{/if}
|
|
218
|
+
{/snippet}
|
|
219
|
+
{#snippet overrideCell(value, column)}
|
|
220
|
+
{#if column.id === "id" && !value}
|
|
221
|
+
<div class="text-muted-foreground">AUTO GENERATED</div>
|
|
222
|
+
{:else}
|
|
223
|
+
<FieldCell
|
|
224
|
+
{collectionName}
|
|
225
|
+
fieldName={column.id}
|
|
226
|
+
{value}
|
|
227
|
+
entry={column}
|
|
228
|
+
/>
|
|
229
|
+
{/if}
|
|
230
|
+
{/snippet}
|
|
231
|
+
{#snippet collapsible(entry, index)}
|
|
232
|
+
{#if entry.id}
|
|
233
|
+
<ChildRecords
|
|
234
|
+
{collectionName}
|
|
235
|
+
recordId={entry.id}
|
|
236
|
+
width={tableWidth}
|
|
237
|
+
unifiedBgColor="bg-soft"
|
|
238
|
+
/>
|
|
239
|
+
{:else}
|
|
240
|
+
<SubRecords
|
|
241
|
+
{collectionName}
|
|
242
|
+
bind:parentEntry={entries[index]}
|
|
243
|
+
width={tableWidth}
|
|
244
|
+
/>
|
|
245
|
+
{/if}
|
|
246
|
+
{/snippet}
|
|
247
|
+
</Table>
|
|
248
|
+
</div>
|
|
249
|
+
{/if}
|
|
250
|
+
</div>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { ctx } from "$lib/store.svelte";
|
|
3
|
+
import CreateManyView from "./createManyView.svelte";
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
collectionName: string;
|
|
7
|
+
parentEntry: any;
|
|
8
|
+
width: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let { collectionName, width, parentEntry = $bindable() }: Props = $props();
|
|
12
|
+
|
|
13
|
+
const childrenRelations = ctx.meta.relations.filter(
|
|
14
|
+
(relation) => relation.to.collection === collectionName,
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
// filling the children properties
|
|
18
|
+
for (let index = 0; index < childrenRelations.length; index++) {
|
|
19
|
+
const relation = childrenRelations[index];
|
|
20
|
+
const childCollection = relation.from.collection;
|
|
21
|
+
if (!parentEntry[childCollection]) {
|
|
22
|
+
parentEntry[childCollection] = [];
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<div class="flex">
|
|
28
|
+
<div class="flex justify-center border-r" style="width: 40px;"></div>
|
|
29
|
+
<div class="flex flex-col" style="width: {width - 40}px;">
|
|
30
|
+
{#each childrenRelations as relation, index}
|
|
31
|
+
{@const lastRow = childrenRelations.length - 1 === index}
|
|
32
|
+
{@const fromCollection = relation.from.collection}
|
|
33
|
+
{@const fromField = relation.from.field}
|
|
34
|
+
<div class="overflow-hidden">
|
|
35
|
+
<CreateManyView
|
|
36
|
+
collectionName={fromCollection}
|
|
37
|
+
bind:entries={parentEntry[fromCollection]}
|
|
38
|
+
parentRecord={{
|
|
39
|
+
id: parentEntry.id,
|
|
40
|
+
collectionName: collectionName,
|
|
41
|
+
}}
|
|
42
|
+
class="border-none"
|
|
43
|
+
expanded={false}
|
|
44
|
+
/>
|
|
45
|
+
</div>
|
|
46
|
+
{/each}
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|