diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(entity)/views/layouts/sidesheet.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(entity)/views/layouts/sidesheet.svelte index 3f38779eb..1e7c3adb6 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(entity)/views/layouts/sidesheet.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(entity)/views/layouts/sidesheet.svelte @@ -11,6 +11,7 @@ let { show = $bindable(false), title, + headerEnd, closeOnBlur = false, submit, cancel, @@ -51,6 +52,7 @@ | undefined; children?: Snippet; footer?: Snippet | null; + headerEnd?: Snippet | null; } & HTMLAttributes = $props(); let form: Form; @@ -90,6 +92,8 @@ {/if} {/if} + + {@render headerEnd?.()} diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(entity)/views/layouts/spreadsheet.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(entity)/views/layouts/spreadsheet.svelte index aac0d131e..7dfc29215 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(entity)/views/layouts/spreadsheet.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(entity)/views/layouts/spreadsheet.svelte @@ -8,11 +8,24 @@ let { children, noSqlEditor, + sideSheetHeaderAction, + sideSheetOptions = null, showEditorSideSheet = $bindable(false) }: { children: Snippet; noSqlEditor?: Snippet; + sideSheetHeaderAction?: Snippet; showEditorSideSheet?: boolean; + sideSheetOptions?: { + sideSheetTitle?: string; + submit?: + | { + text: string; + disabled?: boolean; + onClick?: () => boolean | void | Promise; + } + | undefined; + }; } = $props(); let spreadsheetWrapper: HTMLDivElement; @@ -133,12 +146,14 @@ {:else} + submit={sideSheetOptions?.submit} + title={sideSheetOptions?.sideSheetTitle ?? 'Edit document'}> {@render noSqlEditor?.()} + + {#snippet headerEnd()} + {@render sideSheetHeaderAction?.()} + {/snippet} {/if} diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/(components)/editor/view.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/(components)/editor/view.svelte index 96a0e8b72..cd6f8de03 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/(components)/editor/view.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/(components)/editor/view.svelte @@ -73,12 +73,13 @@ data?: JsonValue; isSaving?: boolean; loading?: boolean; - onChange?: (newData: JsonValue) => Promise | void; + onChange?: (newData: JsonValue, hasChanged: boolean) => Promise | void; onSave?: (newData: JsonValue) => Promise | void; readonly?: boolean; wrapLines?: boolean; errorInPlace?: boolean; ctrlSave?: boolean; + showHeaderActions?: boolean; } let { @@ -91,7 +92,8 @@ readonly = false, wrapLines = true, errorInPlace = true, - ctrlSave = false + ctrlSave = false, + showHeaderActions = true }: Props = $props(); let editorContainer: HTMLDivElement = $state(null); @@ -794,7 +796,7 @@ } data = parsed; - onChange?.(parsed); + onChange?.(parsed, hasDataChanged); lastExpectedContent = dataToString(parsed); }, DEBOUNCE_DELAY); }), @@ -897,65 +899,67 @@
-
- {#if loading} - - {/if} + {#if showHeaderActions} +
+ {#if loading} + + {/if} - - {#if documentId && !loading} -
- {truncateId(documentId)} + + {#if documentId && !loading} +
+ {truncateId(documentId)} +
+ {/if} +
+ + {#if errorMessage && !$isSmallViewport && !loading} +
+ {errorMessage}
{/if} - - {#if errorMessage && !$isSmallViewport && !loading} -
- {errorMessage} -
- {/if} + {#if documentId} + + + - {#if documentId} - - - + Save + - Save - + + - - - - {tooltipMessage} - - - {/if} -
+ {tooltipMessage} + +
+ {/if} +
+ {/if} {#if errorMessage && $isSmallViewport && !loading}
@@ -1053,7 +1057,7 @@ border-bottom: 1px solid var(--border-neutral); &.mobile { - background: var(--bgcolor-error); + background: var(--bgcolor-error-weak); } .error-message { diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/spreadsheet.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/spreadsheet.svelte index ef6c78731..a8e012210 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/spreadsheet.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/spreadsheet.svelte @@ -25,6 +25,7 @@ import { IconCalendar, IconDotsHorizontal, + IconDuplicate, IconFingerPrint } from '@appwrite.io/pink-icons-svelte'; import { isSmallViewport, isTabletViewport } from '$lib/stores/viewport'; @@ -411,9 +412,10 @@ await invalidate(Dependencies.DOCUMENTS); noSqlDocument.update(() => { return { - isNew: false, show: false, - document: {} + isNew: false, + document: {}, + hasDataChanged: false }; }); @@ -514,7 +516,15 @@ + bind:showEditorSideSheet={$noSqlDocument.show} + sideSheetOptions={{ + sideSheetTitle: $noSqlDocument.document?.$id, + submit: { + text: 'Update', + disabled: !$noSqlDocument.hasDataChanged, + onClick: async () => await createOrUpdateDocument($noSqlDocument.document) + } + }}> {#key $spreadsheetRenderKey} + isSelected={$noSqlDocument?.document?.$id === document.$id}> {#each $collectionColumns as { id: columnId } (columnId)} {#if columnId === '$id'} @@ -705,9 +715,30 @@ isNew={$noSqlDocument.isNew} loading={$noSqlDocument.loading} bind:data={$noSqlDocument.document} + showHeaderActions={!$isSmallViewport} + onChange={(_, hasDataChanged) => { + $noSqlDocument.hasDataChanged = hasDataChanged; + }} onSave={async (document) => await createOrUpdateDocument(document)} /> {/snippet} + {#snippet sideSheetHeaderAction()} + { + await copy(JSON.stringify($noSqlDocument.document, null, 2)); + addNotification({ + type: 'success', + message: 'Document copied', + timeout: 1250 + }); + }}> + + + {/snippet} + {#if selectedDocuments.length > 0}
diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/store.ts b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/store.ts index c4057c018..0b868b69e 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/store.ts +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/store.ts @@ -22,16 +22,18 @@ export const sortState = writable({ export const noSqlDocument = writable<{ show: boolean; - document?: Models.Document | object; + document?: Models.Document | (object & { $id?: string }); isNew?: boolean; loading?: boolean; documentId?: string /* for loading from a given id */; + hasDataChanged?: boolean; }>({ show: false, document: null, isNew: false, loading: false, - documentId: null + documentId: null, + hasDataChanged: false }); export const documentPermissionSheet = writable({