@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.
Files changed (208) hide show
  1. package/.env.example +1 -0
  2. package/.storybook/main.ts +31 -0
  3. package/.storybook/preview.ts +21 -0
  4. package/.storybook/vitest.setup.ts +7 -0
  5. package/README.md +47 -0
  6. package/components.json +16 -0
  7. package/docker-entrypoint.sh +7 -0
  8. package/dockerfile +27 -0
  9. package/index.html +13 -0
  10. package/package.json +77 -0
  11. package/public/lobb.svg +15 -0
  12. package/src/Studio.svelte +150 -0
  13. package/src/app.css +121 -0
  14. package/src/components-export.ts +21 -0
  15. package/src/extensions/extension.types.ts +93 -0
  16. package/src/extensions/extensionUtils.ts +192 -0
  17. package/src/lib/Lobb.ts +241 -0
  18. package/src/lib/components/LlmButton.svelte +136 -0
  19. package/src/lib/components/alertView.svelte +20 -0
  20. package/src/lib/components/breadCrumbs.svelte +60 -0
  21. package/src/lib/components/combobox.svelte +92 -0
  22. package/src/lib/components/confirmationDialog/confirmationDialog.svelte +33 -0
  23. package/src/lib/components/confirmationDialog/store.svelte.ts +28 -0
  24. package/src/lib/components/createManyButton.svelte +107 -0
  25. package/src/lib/components/dataTable/childRecords.svelte +140 -0
  26. package/src/lib/components/dataTable/dataTable.svelte +223 -0
  27. package/src/lib/components/dataTable/fieldCell.svelte +74 -0
  28. package/src/lib/components/dataTable/filter.svelte +282 -0
  29. package/src/lib/components/dataTable/filterButton.svelte +39 -0
  30. package/src/lib/components/dataTable/footer.svelte +84 -0
  31. package/src/lib/components/dataTable/header.svelte +154 -0
  32. package/src/lib/components/dataTable/sort.svelte +171 -0
  33. package/src/lib/components/dataTable/sortButton.svelte +36 -0
  34. package/src/lib/components/dataTable/table.svelte +337 -0
  35. package/src/lib/components/dataTable/utils.ts +127 -0
  36. package/src/lib/components/detailView/create/children.svelte +68 -0
  37. package/src/lib/components/detailView/create/createDetailView.svelte +226 -0
  38. package/src/lib/components/detailView/create/createDetailViewButton.svelte +32 -0
  39. package/src/lib/components/detailView/create/createManyView.svelte +250 -0
  40. package/src/lib/components/detailView/create/subRecords.svelte +48 -0
  41. package/src/lib/components/detailView/detailViewForm.svelte +104 -0
  42. package/src/lib/components/detailView/fieldCustomInput.svelte +23 -0
  43. package/src/lib/components/detailView/fieldInput.svelte +287 -0
  44. package/src/lib/components/detailView/fieldInputReplacement.svelte +199 -0
  45. package/src/lib/components/detailView/store.svelte.ts +61 -0
  46. package/src/lib/components/detailView/update/children.svelte +94 -0
  47. package/src/lib/components/detailView/update/updateDetailView.svelte +175 -0
  48. package/src/lib/components/detailView/update/updateDetailViewButton.svelte +32 -0
  49. package/src/lib/components/detailView/utils.ts +177 -0
  50. package/src/lib/components/diffViewer.svelte +102 -0
  51. package/src/lib/components/drawer.svelte +28 -0
  52. package/src/lib/components/extensionsComponents.svelte +31 -0
  53. package/src/lib/components/foreingKeyInput.svelte +80 -0
  54. package/src/lib/components/header.svelte +45 -0
  55. package/src/lib/components/loadingTypesForMonacoEditor.ts +36 -0
  56. package/src/lib/components/miniSidebar.svelte +238 -0
  57. package/src/lib/components/monacoEditor.svelte +181 -0
  58. package/src/lib/components/rangeCalendarButton.svelte +257 -0
  59. package/src/lib/components/selectRecord.svelte +126 -0
  60. package/src/lib/components/setServerPage.svelte +48 -0
  61. package/src/lib/components/sidebar/index.ts +4 -0
  62. package/src/lib/components/sidebar/sidebar.svelte +149 -0
  63. package/src/lib/components/sidebar/sidebarElements.svelte +144 -0
  64. package/src/lib/components/sidebar/sidebarTrigger.svelte +33 -0
  65. package/src/lib/components/singletone.svelte +69 -0
  66. package/src/lib/components/ui/accordion/accordion-content.svelte +22 -0
  67. package/src/lib/components/ui/accordion/accordion-item.svelte +12 -0
  68. package/src/lib/components/ui/accordion/accordion-trigger.svelte +31 -0
  69. package/src/lib/components/ui/accordion/index.ts +17 -0
  70. package/src/lib/components/ui/alert/alert-description.svelte +16 -0
  71. package/src/lib/components/ui/alert/alert-title.svelte +24 -0
  72. package/src/lib/components/ui/alert/alert.svelte +39 -0
  73. package/src/lib/components/ui/alert/index.ts +14 -0
  74. package/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte +13 -0
  75. package/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte +17 -0
  76. package/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte +26 -0
  77. package/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte +16 -0
  78. package/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte +20 -0
  79. package/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte +20 -0
  80. package/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte +19 -0
  81. package/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte +18 -0
  82. package/src/lib/components/ui/alert-dialog/index.ts +40 -0
  83. package/src/lib/components/ui/breadcrumb/breadcrumb-ellipsis.svelte +23 -0
  84. package/src/lib/components/ui/breadcrumb/breadcrumb-item.svelte +16 -0
  85. package/src/lib/components/ui/breadcrumb/breadcrumb-link.svelte +31 -0
  86. package/src/lib/components/ui/breadcrumb/breadcrumb-list.svelte +23 -0
  87. package/src/lib/components/ui/breadcrumb/breadcrumb-page.svelte +23 -0
  88. package/src/lib/components/ui/breadcrumb/breadcrumb-separator.svelte +27 -0
  89. package/src/lib/components/ui/breadcrumb/breadcrumb.svelte +15 -0
  90. package/src/lib/components/ui/breadcrumb/index.ts +25 -0
  91. package/src/lib/components/ui/button/button.svelte +110 -0
  92. package/src/lib/components/ui/button/index.ts +17 -0
  93. package/src/lib/components/ui/checkbox/checkbox.svelte +35 -0
  94. package/src/lib/components/ui/checkbox/index.ts +6 -0
  95. package/src/lib/components/ui/command/command-dialog.svelte +35 -0
  96. package/src/lib/components/ui/command/command-empty.svelte +12 -0
  97. package/src/lib/components/ui/command/command-group.svelte +31 -0
  98. package/src/lib/components/ui/command/command-input.svelte +25 -0
  99. package/src/lib/components/ui/command/command-item.svelte +19 -0
  100. package/src/lib/components/ui/command/command-link-item.svelte +19 -0
  101. package/src/lib/components/ui/command/command-list.svelte +16 -0
  102. package/src/lib/components/ui/command/command-separator.svelte +12 -0
  103. package/src/lib/components/ui/command/command-shortcut.svelte +20 -0
  104. package/src/lib/components/ui/command/command.svelte +21 -0
  105. package/src/lib/components/ui/command/index.ts +40 -0
  106. package/src/lib/components/ui/dialog/dialog-content.svelte +38 -0
  107. package/src/lib/components/ui/dialog/dialog-description.svelte +16 -0
  108. package/src/lib/components/ui/dialog/dialog-footer.svelte +20 -0
  109. package/src/lib/components/ui/dialog/dialog-header.svelte +20 -0
  110. package/src/lib/components/ui/dialog/dialog-overlay.svelte +19 -0
  111. package/src/lib/components/ui/dialog/dialog-title.svelte +16 -0
  112. package/src/lib/components/ui/dialog/index.ts +37 -0
  113. package/src/lib/components/ui/input/index.ts +7 -0
  114. package/src/lib/components/ui/input/input.svelte +46 -0
  115. package/src/lib/components/ui/label/index.ts +7 -0
  116. package/src/lib/components/ui/label/label.svelte +19 -0
  117. package/src/lib/components/ui/popover/index.ts +17 -0
  118. package/src/lib/components/ui/popover/popover-content.svelte +28 -0
  119. package/src/lib/components/ui/range-calendar/index.ts +30 -0
  120. package/src/lib/components/ui/range-calendar/range-calendar-cell.svelte +19 -0
  121. package/src/lib/components/ui/range-calendar/range-calendar-day.svelte +35 -0
  122. package/src/lib/components/ui/range-calendar/range-calendar-grid-body.svelte +12 -0
  123. package/src/lib/components/ui/range-calendar/range-calendar-grid-head.svelte +12 -0
  124. package/src/lib/components/ui/range-calendar/range-calendar-grid-row.svelte +12 -0
  125. package/src/lib/components/ui/range-calendar/range-calendar-grid.svelte +16 -0
  126. package/src/lib/components/ui/range-calendar/range-calendar-head-cell.svelte +16 -0
  127. package/src/lib/components/ui/range-calendar/range-calendar-header.svelte +16 -0
  128. package/src/lib/components/ui/range-calendar/range-calendar-heading.svelte +16 -0
  129. package/src/lib/components/ui/range-calendar/range-calendar-months.svelte +20 -0
  130. package/src/lib/components/ui/range-calendar/range-calendar-next-button.svelte +27 -0
  131. package/src/lib/components/ui/range-calendar/range-calendar-prev-button.svelte +27 -0
  132. package/src/lib/components/ui/range-calendar/range-calendar.svelte +57 -0
  133. package/src/lib/components/ui/select/index.ts +34 -0
  134. package/src/lib/components/ui/select/select-content.svelte +38 -0
  135. package/src/lib/components/ui/select/select-group-heading.svelte +16 -0
  136. package/src/lib/components/ui/select/select-item.svelte +37 -0
  137. package/src/lib/components/ui/select/select-scroll-down-button.svelte +19 -0
  138. package/src/lib/components/ui/select/select-scroll-up-button.svelte +19 -0
  139. package/src/lib/components/ui/select/select-separator.svelte +13 -0
  140. package/src/lib/components/ui/select/select-trigger.svelte +24 -0
  141. package/src/lib/components/ui/separator/index.ts +7 -0
  142. package/src/lib/components/ui/separator/separator.svelte +22 -0
  143. package/src/lib/components/ui/skeleton/index.ts +7 -0
  144. package/src/lib/components/ui/skeleton/skeleton.svelte +22 -0
  145. package/src/lib/components/ui/sonner/index.ts +1 -0
  146. package/src/lib/components/ui/sonner/sonner.svelte +20 -0
  147. package/src/lib/components/ui/switch/index.ts +7 -0
  148. package/src/lib/components/ui/switch/switch.svelte +27 -0
  149. package/src/lib/components/ui/textarea/index.ts +7 -0
  150. package/src/lib/components/ui/textarea/textarea.svelte +22 -0
  151. package/src/lib/components/ui/tooltip/index.ts +18 -0
  152. package/src/lib/components/ui/tooltip/tooltip-content.svelte +21 -0
  153. package/src/lib/components/workflowEditor.svelte +187 -0
  154. package/src/lib/eventSystem.ts +38 -0
  155. package/src/lib/index.ts +40 -0
  156. package/src/lib/store.svelte.ts +21 -0
  157. package/src/lib/store.types.ts +28 -0
  158. package/src/lib/utils.ts +84 -0
  159. package/src/main.ts +18 -0
  160. package/src/routes/collections/collection.svelte +46 -0
  161. package/src/routes/collections/collections.svelte +43 -0
  162. package/src/routes/data_model/dataModel.svelte +40 -0
  163. package/src/routes/data_model/flow.css +22 -0
  164. package/src/routes/data_model/flow.svelte +82 -0
  165. package/src/routes/data_model/syncManager.svelte +93 -0
  166. package/src/routes/data_model/utils.ts +35 -0
  167. package/src/routes/extensions/extension.svelte +16 -0
  168. package/src/routes/home.svelte +36 -0
  169. package/src/routes/workflows/workflows.svelte +135 -0
  170. package/src/stories/Configure.mdx +364 -0
  171. package/src/stories/assets/accessibility.png +0 -0
  172. package/src/stories/assets/accessibility.svg +1 -0
  173. package/src/stories/assets/addon-library.png +0 -0
  174. package/src/stories/assets/assets.png +0 -0
  175. package/src/stories/assets/avif-test-image.avif +0 -0
  176. package/src/stories/assets/context.png +0 -0
  177. package/src/stories/assets/discord.svg +1 -0
  178. package/src/stories/assets/docs.png +0 -0
  179. package/src/stories/assets/figma-plugin.png +0 -0
  180. package/src/stories/assets/github.svg +1 -0
  181. package/src/stories/assets/share.png +0 -0
  182. package/src/stories/assets/styling.png +0 -0
  183. package/src/stories/assets/testing.png +0 -0
  184. package/src/stories/assets/theming.png +0 -0
  185. package/src/stories/assets/tutorials.svg +1 -0
  186. package/src/stories/assets/youtube.svg +1 -0
  187. package/src/stories/detailView/detailViewForm.stories.svelte +79 -0
  188. package/src/stories/examples/Button.stories.svelte +31 -0
  189. package/src/stories/examples/Button.svelte +30 -0
  190. package/src/stories/examples/Header.stories.svelte +26 -0
  191. package/src/stories/examples/Header.svelte +45 -0
  192. package/src/stories/examples/Page.stories.svelte +29 -0
  193. package/src/stories/examples/Page.svelte +70 -0
  194. package/src/stories/examples/button.css +30 -0
  195. package/src/stories/examples/header.css +32 -0
  196. package/src/stories/examples/page.css +68 -0
  197. package/src/vite-env.d.ts +2 -0
  198. package/svelte.config.js +7 -0
  199. package/todo.md +24 -0
  200. package/tsconfig.app.json +25 -0
  201. package/tsconfig.json +14 -0
  202. package/tsconfig.node.json +24 -0
  203. package/vite-plugin-contextual-lib.js +66 -0
  204. package/vite.build.svelte.config.ts +18 -0
  205. package/vite.config.ts +84 -0
  206. package/vite.extension.config.ts +81 -0
  207. package/vite_utils.ts +28 -0
  208. 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>