From f066be49d022466f5296029d0f1dabdacfc1c5c0 Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Sun, 7 Sep 2025 14:24:34 +0530 Subject: [PATCH 001/150] add missing filter elements for status and providerType columns --- src/lib/components/filters/quickFilters.ts | 8 ++- src/lib/components/filters/setFilters.ts | 7 ++- .../messaging/+page.svelte | 54 +++++++++++++++++-- 3 files changed, 63 insertions(+), 6 deletions(-) diff --git a/src/lib/components/filters/quickFilters.ts b/src/lib/components/filters/quickFilters.ts index dce8e823d..29746e20c 100644 --- a/src/lib/components/filters/quickFilters.ts +++ b/src/lib/components/filters/quickFilters.ts @@ -47,7 +47,13 @@ export function addFilterAndApply( addSizeFilter(value, colId, columns); } else if (colId === 'statusCode') { addStatusCodeFilter(value, colId, columns); - } else if (colId === '$createdAt' || colId === '$updatedAt' || colId === 'buildDuration') { + } else if ( + colId === '$createdAt' || + colId === '$updatedAt' || + colId === 'buildDuration' || + colId === 'scheduledAt' || + colId === 'deliveredAt' + ) { addDateFilter(value, colId, columns); } else { addFilter(columns, colId, operator, value, arrayValues); diff --git a/src/lib/components/filters/setFilters.ts b/src/lib/components/filters/setFilters.ts index b7e0f18c3..68529cca1 100644 --- a/src/lib/components/filters/setFilters.ts +++ b/src/lib/components/filters/setFilters.ts @@ -20,7 +20,12 @@ export function setFilters(localTags: TagValue[], filterCols: FilterData[], $col setSizeFilter(filter, $columns); } else if (id?.includes('statuscode')) { setStatusCodeFilter(filter, $columns); - } else if (id === '$createdat' || id === '$updatedat') { + } else if ( + id === '$createdat' || + id === '$updatedat' || + id === 'scheduledat' || + id === 'deliveredat' + ) { setDateFilter(filter, $columns); } else { setFilterData(filter); diff --git a/src/routes/(console)/project-[region]-[project]/messaging/+page.svelte b/src/routes/(console)/project-[region]-[project]/messaging/+page.svelte index 1931f56ff..fcc732593 100644 --- a/src/routes/(console)/project-[region]-[project]/messaging/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/messaging/+page.svelte @@ -49,10 +49,56 @@ filter: false, width: { min: 140 } }, - { id: 'providerType', title: 'Type', type: 'string', width: { min: 100 } }, - { id: 'status', title: 'Status', type: 'string', width: { min: 120 } }, - { id: 'scheduledAt', title: 'Scheduled at', type: 'datetime', width: { min: 120 } }, - { id: 'deliveredAt', title: 'Delivered at', type: 'datetime', width: { min: 120 } } + { + id: 'providerType', + title: 'Type', + type: 'string', + width: { min: 100 }, + array: true, + format: 'enum', + elements: [ + { value: 'email', label: 'Email' }, + { value: 'sms', label: 'SMS' }, + { value: 'push', label: 'Push' } + ] + }, + { + id: 'status', + title: 'Status', + type: 'enum', + width: { min: 120 }, + array: true, + format: 'enum', + elements: ['draft', 'scheduled', 'processing', 'sent', 'failed'] + }, + { + id: 'scheduledAt', + title: 'Scheduled at', + type: 'datetime', + width: { min: 120 }, + format: 'datetime', + elements: [ + { value: 5 * 60 * 1000, label: 'last 5 minutes' }, + { value: 60 * 60 * 1000, label: 'last 1 hour' }, + { value: 24 * 60 * 60 * 1000, label: 'last 24 hours' }, + { value: 7 * 24 * 60 * 60 * 1000, label: 'last 7 days' }, + { value: 30 * 24 * 60 * 60 * 1000, label: 'last 30 days' } + ] + }, + { + id: 'deliveredAt', + title: 'Delivered at', + type: 'datetime', + width: { min: 120 }, + format: 'datetime', + elements: [ + { value: 5 * 60 * 1000, label: 'last 5 minutes' }, + { value: 60 * 60 * 1000, label: 'last 1 hour' }, + { value: 24 * 60 * 60 * 1000, label: 'last 24 hours' }, + { value: 7 * 24 * 60 * 60 * 1000, label: 'last 7 days' }, + { value: 30 * 24 * 60 * 60 * 1000, label: 'last 30 days' } + ] + } ]); const region = page.params.region; From 1a899bfb4a8aa53a3c31522a4a913a81d3d0420d Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Tue, 9 Sep 2025 18:51:37 +0530 Subject: [PATCH 002/150] tooltp added over disabled --- .../organization-[organization]/+page.svelte | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src/routes/(console)/organization-[organization]/+page.svelte b/src/routes/(console)/organization-[organization]/+page.svelte index 69a84cd8a..3c0da4065 100644 --- a/src/routes/(console)/organization-[organization]/+page.svelte +++ b/src/routes/(console)/organization-[organization]/+page.svelte @@ -84,6 +84,9 @@ (isCloud && $readOnly && !GRACE_PERIOD_OVERRIDE) || !$canWriteProjects; + $: reachedProjectLimit = isCloud && getServiceLimit('projects') <= data.allProjectsCount; + $: projectsLimit = getServiceLimit('projects'); + $: $registerCommands([ { label: 'Create project', @@ -129,13 +132,27 @@ {#if $canWriteProjects} - + {#if projectCreationDisabled && reachedProjectLimit} + +
+ +
+ + You have reached your limit of {projectsLimit} projects. + +
+ {:else} + + {/if} {/if} From 521b2899f656fd2243b6d4b75bc0a0bf66fda18c Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Tue, 9 Sep 2025 18:55:34 +0530 Subject: [PATCH 003/150] fix: project disabled on creating organization --- src/routes/(console)/organization-[organization]/+page.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/(console)/organization-[organization]/+page.svelte b/src/routes/(console)/organization-[organization]/+page.svelte index 3c0da4065..50ce72fab 100644 --- a/src/routes/(console)/organization-[organization]/+page.svelte +++ b/src/routes/(console)/organization-[organization]/+page.svelte @@ -80,11 +80,11 @@ } $: projectCreationDisabled = - (isCloud && getServiceLimit('projects') <= data.allProjectsCount) || + (isCloud && getServiceLimit('projects') <= data.projects.total) || (isCloud && $readOnly && !GRACE_PERIOD_OVERRIDE) || !$canWriteProjects; - $: reachedProjectLimit = isCloud && getServiceLimit('projects') <= data.allProjectsCount; + $: reachedProjectLimit = isCloud && getServiceLimit('projects') <= data.projects.total; $: projectsLimit = getServiceLimit('projects'); $: $registerCommands([ From 1e99b585f60607f167a527f0a419b599bff9b8a0 Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Tue, 9 Sep 2025 19:05:29 +0530 Subject: [PATCH 004/150] lint issue --- src/routes/(console)/organization-[organization]/+page.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/(console)/organization-[organization]/+page.svelte b/src/routes/(console)/organization-[organization]/+page.svelte index 50ce72fab..e223ec1fc 100644 --- a/src/routes/(console)/organization-[organization]/+page.svelte +++ b/src/routes/(console)/organization-[organization]/+page.svelte @@ -141,7 +141,7 @@ - You have reached your limit of {projectsLimit} projects. + You have reached your limit of {projectsLimit} projects. {:else} From c778711c7b058cb10319ce5ea6b6b5b62ba9a2cf Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 11 Sep 2025 16:22:17 +0530 Subject: [PATCH 005/150] fix: preferences fallback logic. --- src/lib/stores/preferences.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/stores/preferences.ts b/src/lib/stores/preferences.ts index 4e3e3224f..36419be28 100644 --- a/src/lib/stores/preferences.ts +++ b/src/lib/stores/preferences.ts @@ -220,7 +220,8 @@ function createPreferences() { loadTeamPrefs: loadTeamPreferences, getDisplayNames: (tableId: string) => { - return teamPreferences?.displayNames?.[tableId] ?? ['$id']; + const names = teamPreferences?.displayNames?.[tableId]; + return Array.isArray(names) && names.length > 0 ? names : ['$id']; }, setDisplayNames: async ( From e17ae664828f1fe848e336158fcc446c794a51c6 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 11 Sep 2025 17:00:29 +0530 Subject: [PATCH 006/150] add: relationship type on columns page cell. --- .../database-[database]/subNavigation.svelte | 4 ++- .../table-[table]/columns/+page.svelte | 26 ++++++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/subNavigation.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/subNavigation.svelte index a33c4e15b..535304213 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/subNavigation.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/subNavigation.svelte @@ -41,7 +41,9 @@ tables: [] }); - const sortedTables = $derived.by(() => tables?.tables); + const sortedTables = $derived.by( + () => tables?.tables?.slice().sort((a, b) => a.name.localeCompare(b.name)) + ); const selectedTable = $derived.by(() => sortedTables?.find((table: Models.Table) => table.$id === tableId) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/columns/+page.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/columns/+page.svelte index 59df944c3..24bab8fad 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/columns/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/columns/+page.svelte @@ -164,6 +164,24 @@ } } + function getRelationshipTypeForColumn(column: Columns): string | null { + if (!isRelationship(column)) { + return null; + } + + const relationshipMap = { + oneToOne: 'One to one', + oneToMany: 'One to many', + manyToOne: 'Many to one', + manyToMany: 'Many to many' + }; + + const relationType = (column as Models.ColumnRelationship).relationType; + const formattedType = relationshipMap[relationType] || relationType; + + return `Type: ${formattedType}`; + } + onDestroy(() => ($showCreateColumnSheet.show = false)); $effect(() => { @@ -222,7 +240,6 @@ - {#each updatedColumnsForSheet as column, index} {@const option = columnOptions.find((option) => option.type === column.type)} {@const isSelectable = @@ -290,12 +307,19 @@ {@const minMaxSize = getMinMaxSizeForColumn(column)} + {@const relationType = getRelationshipTypeForColumn(column)} {#if minMaxSize} {minMaxSize} + {:else if relationType} + + {relationType} + {/if} From b3b18061164b20aad982b86ac50fedbe1dd0e3d1 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 11 Sep 2025 17:00:51 +0530 Subject: [PATCH 007/150] lint. --- .../databases/database-[database]/subNavigation.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/subNavigation.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/subNavigation.svelte index 535304213..8ab8b314e 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/subNavigation.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/subNavigation.svelte @@ -41,8 +41,8 @@ tables: [] }); - const sortedTables = $derived.by( - () => tables?.tables?.slice().sort((a, b) => a.name.localeCompare(b.name)) + const sortedTables = $derived.by(() => + tables?.tables?.slice().sort((a, b) => a.name.localeCompare(b.name)) ); const selectedTable = $derived.by(() => From 594a673337a421f713753039014679ff5175b92f Mon Sep 17 00:00:00 2001 From: Darshan Date: Mon, 15 Sep 2025 20:02:39 +0530 Subject: [PATCH 008/150] feat: ai suggestions [wip]. --- .../database-[database]/createTable.svelte | 9 +++ .../database-[database]/icon/ai.svelte | 68 +++++++++++++++++++ .../database-[database]/suggestions.svelte | 55 +++++++++++++++ 3 files changed, 132 insertions(+) create mode 100644 src/routes/(console)/project-[region]-[project]/databases/database-[database]/icon/ai.svelte create mode 100644 src/routes/(console)/project-[region]-[project]/databases/database-[database]/suggestions.svelte diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/createTable.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/createTable.svelte index 4e42ee442..2a5f509eb 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/createTable.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/createTable.svelte @@ -9,6 +9,8 @@ import { ID } from '@appwrite.io/console'; import { createEventDispatcher } from 'svelte'; import { subNavigation } from '$lib/stores/database'; + import Suggestions from './suggestions.svelte'; + import type { TableColumnSuggestions } from './suggestions.svelte'; let { showCreate = $bindable(false) @@ -24,6 +26,11 @@ let touchedId = $state(false); let error: string = $state(null); + let suggestions: TableColumnSuggestions = $state({ + enabled: false, + context: null + }) + const create = async () => { error = null; try { @@ -104,6 +111,8 @@ } }} /> + + diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/icon/ai.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/icon/ai.svelte new file mode 100644 index 000000000..07614291c --- /dev/null +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/icon/ai.svelte @@ -0,0 +1,68 @@ + +
+ + + + + + + + + + + + + + + + + + + +
+ + diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/suggestions.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/suggestions.svelte new file mode 100644 index 000000000..38cfbec08 --- /dev/null +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/suggestions.svelte @@ -0,0 +1,55 @@ + + + + + + + + + + Smart column suggestions + + + + + + Enable AI to suggest useful columns based on your table name + + + + + {#if suggestions.enabled} +
+ +
+ {/if} +
+
From 3ed568d777b33f37f6f5a44d012eb0dffb6aa885 Mon Sep 17 00:00:00 2001 From: Darshan Date: Tue, 16 Sep 2025 19:42:34 +0530 Subject: [PATCH 009/150] add: some nice stuff. --- package.json | 2 +- pnpm-lock.yaml | 10 +- .../database-[database]/createTable.svelte | 8 +- .../database-[database]/icon/ai.svelte | 19 +- .../databases/database-[database]/store.ts | 12 + .../database-[database]/subNavigation.svelte | 5 +- .../database-[database]/suggestions.svelte | 39 +- .../table-[table]/+page.svelte | 4 + .../layout/suggestionsEmptySheet.svelte | 468 ++++++++++++++++++ 9 files changed, 524 insertions(+), 43 deletions(-) create mode 100644 src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/layout/suggestionsEmptySheet.svelte diff --git a/package.json b/package.json index cd4e9b060..183d358a8 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ }, "dependencies": { "@ai-sdk/svelte": "^1.1.24", - "@appwrite.io/console": "https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@6031134", + "@appwrite.io/console": "https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@2407", "@appwrite.io/pink-icons": "0.25.0", "@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@f4da718", "@appwrite.io/pink-legacy": "^1.0.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 747776222..3a65d67e9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,8 +12,8 @@ importers: specifier: ^1.1.24 version: 1.1.24(svelte@5.25.3)(zod@3.24.3) '@appwrite.io/console': - specifier: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@6031134 - version: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@6031134 + specifier: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@2407 + version: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@2407 '@appwrite.io/pink-icons': specifier: 0.25.0 version: 0.25.0 @@ -260,8 +260,8 @@ packages: '@analytics/type-utils@0.6.2': resolution: {integrity: sha512-TD+xbmsBLyYy/IxFimW/YL/9L2IEnM7/EoV9Aeh56U64Ify8o27HJcKjo38XY9Tcn0uOq1AX3thkKgvtWvwFQg==} - '@appwrite.io/console@https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@6031134': - resolution: {tarball: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@6031134} + '@appwrite.io/console@https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@2407': + resolution: {tarball: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@2407} version: 1.10.0 '@appwrite.io/pink-icons-svelte@2.0.0-RC.1': @@ -3700,7 +3700,7 @@ snapshots: '@analytics/type-utils@0.6.2': {} - '@appwrite.io/console@https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@6031134': {} + '@appwrite.io/console@https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@2407': {} '@appwrite.io/pink-icons-svelte@2.0.0-RC.1(svelte@5.25.3)': dependencies: diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/createTable.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/createTable.svelte index 2a5f509eb..e6d7dd08e 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/createTable.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/createTable.svelte @@ -10,7 +10,6 @@ import { createEventDispatcher } from 'svelte'; import { subNavigation } from '$lib/stores/database'; import Suggestions from './suggestions.svelte'; - import type { TableColumnSuggestions } from './suggestions.svelte'; let { showCreate = $bindable(false) @@ -26,11 +25,6 @@ let touchedId = $state(false); let error: string = $state(null); - let suggestions: TableColumnSuggestions = $state({ - enabled: false, - context: null - }) - const create = async () => { error = null; try { @@ -111,7 +105,7 @@ } }} /> - + diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/icon/ai.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/icon/ai.svelte index 07614291c..27bc31198 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/icon/ai.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/icon/ai.svelte @@ -1,5 +1,8 @@ - -
+ + + -
+ diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/store.ts b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/store.ts index 36e755d77..c06d8be46 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/store.ts +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/store.ts @@ -4,9 +4,21 @@ import type { Models } from '@appwrite.io/console'; import { derived, writable } from 'svelte/store'; import { IconChartBar, IconCloudUpload, IconCog } from '@appwrite.io/pink-icons-svelte'; +export type TableColumnSuggestions = { + enabled: boolean; + thinking: boolean; + context?: string | null; +}; + export const database = derived(page, ($page) => $page.data.database as Models.Database); export const showCreateTable = writable(false); +export const tableColumnSuggestions = writable({ + enabled: false, + context: null, + thinking: false +}); + export const tableViewColumns = writable([ { id: '$id', title: 'Table ID', type: 'string', width: 200 }, { id: 'name', title: 'Name', type: 'string', width: { min: 120 } }, diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/subNavigation.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/subNavigation.svelte index 864a133cc..a9d1abc29 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/subNavigation.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/subNavigation.svelte @@ -145,7 +145,10 @@ - +
diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/suggestions.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/suggestions.svelte index 38cfbec08..0baab1891 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/suggestions.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/suggestions.svelte @@ -1,19 +1,14 @@ @@ -26,11 +21,12 @@ Smart column suggestions - +
+ +
@@ -39,17 +35,22 @@
- {#if suggestions.enabled} + {#if $tableColumnSuggestions.enabled}
+ bind:value={$tableColumnSuggestions.context} + placeholder="Optional: Add context to improve suggestions" />
{/if} + + diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte index 7a52f59e9..f52a0eb0a 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte @@ -29,8 +29,10 @@ import { IconChevronDown, IconChevronUp, IconPlus } from '@appwrite.io/pink-icons-svelte'; import type { Models } from '@appwrite.io/console'; import EmptySheet from './layout/emptySheet.svelte'; + import SuggestionsEmptySheet from './layout/suggestionsEmptySheet.svelte'; import CreateRow from './rows/create.svelte'; import { onDestroy } from 'svelte'; + import { tableColumnSuggestions } from '../store'; export let data: PageData; @@ -215,6 +217,8 @@ } }} /> {/if} + {:else if $tableColumnSuggestions.thinking} + {:else} + import { + Button, + Icon, + Layout, + Spinner, + Spreadsheet, + Typography, + FloatingActionBar + } from '@appwrite.io/pink-svelte'; + import { IconCalendar, IconFingerPrint, IconPlus } from '@appwrite.io/pink-icons-svelte'; + import { isSmallViewport } from '$lib/stores/viewport'; + import { SortButton } from '$lib/components'; + import type { Column } from '$lib/helpers/types'; + import { expandTabs } from '../store'; + import SpreadsheetContainer from './spreadsheet.svelte'; + import { onDestroy, onMount } from 'svelte'; + import { debounce } from '$lib/helpers/debounce'; + import { tableColumnSuggestions } from '../../store'; + + const { customColumns = [] }: { customColumns?: Column[] } = $props(); + + // can also be `__select_undefined` if `$id` needs to be covered on overlay! + const firstColumn = '$id'; + + let resizeObserver: ResizeObserver; + let spreadsheetContainer: HTMLElement; + + let hScroller: HTMLElement | null = null; + let headerElement: HTMLElement | null = null; + let rangeOverlayEl: HTMLDivElement | null = null; + + const baseColProps = { draggable: false, resizable: false }; + + const findHorizontalScroller = (root: HTMLElement | null): HTMLElement | null => { + let el = root as HTMLElement | null; + while (el && el !== document.body) { + const st = getComputedStyle(el); + if ( + (st.overflowX === 'auto' || st.overflowX === 'scroll') && + el.scrollWidth > el.clientWidth + ) { + return el; + } + el = el.parentElement as HTMLElement | null; + } + return null; + }; + + const updateOverlayHeight = () => { + if (!spreadsheetContainer) return; + if (!headerElement || !headerElement.isConnected) { + headerElement = spreadsheetContainer.querySelector('[role="rowheader"]'); + } + if (!headerElement) return; + + const headerRect = headerElement.getBoundingClientRect(); + const containerRect = spreadsheetContainer.getBoundingClientRect(); + const viewportHeight = window.innerHeight; + const dynamicHeightPx = viewportHeight - headerRect.bottom; + const overlayTop = Math.round(headerRect.bottom - containerRect.top); + const overlayHeight = $expandTabs + ? `${dynamicHeightPx}px` + : `calc(${dynamicHeightPx}px - 89px)`; + + spreadsheetContainer.style.setProperty('--overlay-top', `${overlayTop}px`); + spreadsheetContainer.style.setProperty('--overlay-height', overlayHeight); + }; + + // Measure first content header + // and the cell before `actions`|`empty` + const updateOverlayBounds = () => { + if (!spreadsheetContainer) return; + if (!headerElement || !headerElement.isConnected) { + headerElement = spreadsheetContainer.querySelector('[role="rowheader"]'); + if (!headerElement) return; + } + + // hook the actual horizontal scroller once + if (!hScroller || !hScroller.isConnected) { + hScroller = findHorizontalScroller(headerElement); + if (hScroller) hScroller.addEventListener('scroll', debouncedRecalc, { passive: true }); + } + + const containerRect = spreadsheetContainer.getBoundingClientRect(); + const getById = (id: string) => + headerElement!.querySelector( + `[role="cell"][data-header="true"][data-column-id="${id}"]` + ); + + const firstColumnCell = getById(firstColumn); + const actionsCell = getById('actions'); + const emptyCell = getById('empty'); + + // start = first content cell after `firstColumn` + // (skip actions/empty if they were there) + let startCell: HTMLElement | null = null; + if (firstColumnCell) { + let node = firstColumnCell.nextElementSibling as HTMLElement | null; + while ( + node && + (node.dataset.columnId === 'actions' || node.dataset.columnId === 'empty') + ) { + node = node.nextElementSibling as HTMLElement | null; + } + startCell = node; + } + + // end = cell right BEFORE actions; + // else right BEFORE empty; else last content cell + const before = (el: HTMLElement | null) => { + if (!el) return null; + let previous = el.previousElementSibling as HTMLElement | null; + while ( + previous && + (previous.dataset.columnId === firstColumn || + previous.getAttribute('data-select') === 'true') + ) { + previous = previous.previousElementSibling as HTMLElement | null; + } + return previous; + }; + + let endCell = before(actionsCell) ?? before(emptyCell); + if (!endCell) { + const cells = Array.from( + headerElement.querySelectorAll('[role="cell"][data-header="true"]') + ); + for (let index = cells.length - 1; index >= 0; index--) { + const cell = cells[index]; + const cellId = cell.dataset.columnId; + const isSelect = cell.getAttribute('data-select') === 'true'; + if (!isSelect && cellId !== firstColumn && cellId !== 'actions' && cellId !== 'empty') { + endCell = cell; + break; + } + } + } + + if (!startCell || !endCell) { + if (rangeOverlayEl) rangeOverlayEl.style.display = 'none'; + return; + } + + const start = startCell.getBoundingClientRect(); + const end = endCell.getBoundingClientRect(); + const left = Math.round(start.left - containerRect.left); + const width = Math.max(0, Math.round(end.right - end.left)); + + spreadsheetContainer.style.setProperty('--group-left', `${left}px`); + spreadsheetContainer.style.setProperty('--group-width', `${width}px`); + + if (rangeOverlayEl) rangeOverlayEl.style.display = width > 0 ? 'block' : 'none'; + }; + + const recalcAll = () => { + updateOverlayHeight(); + updateOverlayBounds(); + }; + + const debouncedRecalc = debounce(recalcAll, 50); + + onMount(() => { + if (spreadsheetContainer) { + resizeObserver = new ResizeObserver(debouncedRecalc); + resizeObserver.observe(spreadsheetContainer); + } + + requestAnimationFrame(recalcAll); + }); + + onDestroy(() => { + resizeObserver?.disconnect(); + hScroller?.removeEventListener('scroll', debouncedRecalc); + }); + + const getCustomColumns = (): Column[] => + customColumns.map((col: Column) => ({ ...col, width: 180, hide: false, ...baseColProps })); + + const getRowColumns = (): Column[] => [ + { + id: '$id', + title: '$id', + type: 'string', + width: 180, + icon: IconFingerPrint, + ...baseColProps + }, + ...getCustomColumns(), + { + id: '$createdAt', + title: '$createdAt', + type: 'datetime', + width: 180, + icon: IconCalendar, + ...baseColProps + }, + { + id: '$updatedAt', + title: '$updatedAt', + type: 'datetime', + width: 180, + icon: IconCalendar, + ...baseColProps + }, + { + id: 'actions', + title: '', + type: 'string', + icon: IconPlus, + width: customColumns.length ? 555 : 832, + ...baseColProps + }, + { id: 'empty', title: '', type: 'string', ...baseColProps } + ]; + + const spreadsheetColumns = getRowColumns(); + const emptyCells = $derived(($isSmallViewport ? 14 : 17) + (!$expandTabs ? 2 : 0)); + + + + +
+
+ + + {#if $tableColumnSuggestions.thinking} +
+ + + + + + Thinking of column suggestions + + + + + { + $tableColumnSuggestions.context = null; + $tableColumnSuggestions.enabled = false; + $tableColumnSuggestions.thinking = false; + }} + >Cancel + + + +
+ {/if} +
+ + + {}}> + + {#each spreadsheetColumns as column (column.id)} + + {#if column.isAction} + + + + {:else if column.id === 'actions' || column.id === 'empty'} + {column.title} + {:else} + + {column.title} + + + {/if} + + {/each} + + + + +
+
+
+ + From d2761825dd698c2ee66caadcc6885b9569456329 Mon Sep 17 00:00:00 2001 From: Darshan Date: Tue, 16 Sep 2025 19:46:27 +0530 Subject: [PATCH 010/150] add: some nice stuff. --- .../databases/database-[database]/subNavigation.svelte | 1 + 1 file changed, 1 insertion(+) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/subNavigation.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/subNavigation.svelte index a9d1abc29..7fea1eb73 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/subNavigation.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/subNavigation.svelte @@ -145,6 +145,7 @@ + Date: Wed, 17 Sep 2025 16:39:48 +0530 Subject: [PATCH 011/150] update: misc fixes and updates. --- .../database-[database]/subNavigation.svelte | 9 +- .../layout/suggestionsEmptySheet.svelte | 89 ++++++++++--------- 2 files changed, 53 insertions(+), 45 deletions(-) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/subNavigation.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/subNavigation.svelte index 7fea1eb73..554e12e46 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/subNavigation.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/subNavigation.svelte @@ -4,6 +4,8 @@ import { showCreateTable, databaseSubNavigationItems } from './store'; import type { PageData } from './$types'; import { showSubNavigation } from '$lib/stores/layout'; + import { bannerSpacing } from '$lib/layout/headerAlert.svelte'; + import { Icon, Sidebar, @@ -51,6 +53,11 @@ const isMainDatabaseScreen = $derived(page.route.id.endsWith('database-[database]')); + // If banner open, `-1rem` to adjust banner size, else `-70.5px`. + // 70.5px is the size of the container of the banner holder and not just the banner! + // Needed because things vary a bit much on how different browsers treat bottom layouts. + const bottomNavHeight = $derived(`calc(20% ${$bannerSpacing ? '- 1rem' : '- 70.5px'})`); + async function loadTables() { tables = await sdk.forProject(region, project).tablesDB.listTables({ databaseId: databaseId, @@ -149,7 +156,7 @@ + style="bottom: 1rem; position: relative; height: {bottomNavHeight}">
diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/layout/suggestionsEmptySheet.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/layout/suggestionsEmptySheet.svelte index 432339fd3..f138ef4af 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/layout/suggestionsEmptySheet.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/layout/suggestionsEmptySheet.svelte @@ -18,10 +18,20 @@ import { debounce } from '$lib/helpers/debounce'; import { tableColumnSuggestions } from '../../store'; - const { customColumns = [] }: { customColumns?: Column[] } = $props(); + const { + customColumns = [] + }: { + customColumns?: Column[]; + } = $props(); + + /** + * flip this when you want to + * exclude or include the `$id` for colored overlay! + */ + const useFirstColumnAsId = false; // can also be `__select_undefined` if `$id` needs to be covered on overlay! - const firstColumn = '$id'; + const firstColumn = useFirstColumnAsId ? '$id' : '__select_undefined'; let resizeObserver: ResizeObserver; let spreadsheetContainer: HTMLElement; @@ -33,16 +43,17 @@ const baseColProps = { draggable: false, resizable: false }; const findHorizontalScroller = (root: HTMLElement | null): HTMLElement | null => { - let el = root as HTMLElement | null; - while (el && el !== document.body) { - const st = getComputedStyle(el); + let element = root as HTMLElement | null; + while (element && element !== document.body) { + const computedStyles = getComputedStyle(element); if ( - (st.overflowX === 'auto' || st.overflowX === 'scroll') && - el.scrollWidth > el.clientWidth + (computedStyles.overflowX === 'auto' || computedStyles.overflowX === 'scroll') && + element.scrollWidth > element.clientWidth ) { - return el; + return element; } - el = el.parentElement as HTMLElement | null; + + element = element.parentElement as HTMLElement | null; } return null; }; @@ -52,6 +63,7 @@ if (!headerElement || !headerElement.isConnected) { headerElement = spreadsheetContainer.querySelector('[role="rowheader"]'); } + if (!headerElement) return; const headerRect = headerElement.getBoundingClientRect(); @@ -89,8 +101,8 @@ ); const firstColumnCell = getById(firstColumn); - const actionsCell = getById('actions'); - const emptyCell = getById('empty'); + // const emptyCell = getById('empty'); + // const actionsCell = getById('actions'); // start = first content cell after `firstColumn` // (skip actions/empty if they were there) @@ -108,49 +120,38 @@ // end = cell right BEFORE actions; // else right BEFORE empty; else last content cell - const before = (el: HTMLElement | null) => { - if (!el) return null; - let previous = el.previousElementSibling as HTMLElement | null; - while ( - previous && - (previous.dataset.columnId === firstColumn || - previous.getAttribute('data-select') === 'true') - ) { - previous = previous.previousElementSibling as HTMLElement | null; - } - return previous; - }; + // const before = (element: HTMLElement | null) => { + // if (!element) return null; + // let previous = element.previousElementSibling as HTMLElement | null; + // while ( + // previous && + // (previous.dataset.columnId === firstColumn || + // previous.getAttribute('data-select') === 'true') + // ) { + // previous = previous.previousElementSibling as HTMLElement | null; + // } + // return previous; + // }; - let endCell = before(actionsCell) ?? before(emptyCell); - if (!endCell) { - const cells = Array.from( - headerElement.querySelectorAll('[role="cell"][data-header="true"]') - ); - for (let index = cells.length - 1; index >= 0; index--) { - const cell = cells[index]; - const cellId = cell.dataset.columnId; - const isSelect = cell.getAttribute('data-select') === 'true'; - if (!isSelect && cellId !== firstColumn && cellId !== 'actions' && cellId !== 'empty') { - endCell = cell; - break; - } - } - } + // let endCell = before(actionsCell) ?? before(emptyCell); + // let endCell = before(emptyCell); - if (!startCell || !endCell) { + if (!startCell /* || !endCell*/) { if (rangeOverlayEl) rangeOverlayEl.style.display = 'none'; return; } const start = startCell.getBoundingClientRect(); - const end = endCell.getBoundingClientRect(); + // const end = endCell.getBoundingClientRect(); const left = Math.round(start.left - containerRect.left); - const width = Math.max(0, Math.round(end.right - end.left)); + // const width = Math.max(0, Math.round(end.right - start.right)); spreadsheetContainer.style.setProperty('--group-left', `${left}px`); - spreadsheetContainer.style.setProperty('--group-width', `${width}px`); + // spreadsheetContainer.style.setProperty('--group-width', `${width}px`); - if (rangeOverlayEl) rangeOverlayEl.style.display = width > 0 ? 'block' : 'none'; + // if (rangeOverlayEl) { + // rangeOverlayEl.style.display = width > 0 ? 'block' : 'none'; + // } }; const recalcAll = () => { @@ -379,7 +380,7 @@ } & .floating-action-wrapper :global(:first-child) { - left: 55%; + left: 45%; /* change this value if the firstColumn is changed for overlay logic.*/ z-index: 21; } From 7d57d6ae2c5211e4f334d98119d8eecf266685e7 Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Thu, 18 Sep 2025 18:36:40 +0530 Subject: [PATCH 012/150] feat: Add info alert for Free plan users with no archived projects --- .../organization-[organization]/+page.svelte | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/routes/(console)/organization-[organization]/+page.svelte b/src/routes/(console)/organization-[organization]/+page.svelte index 69a84cd8a..dd58e6e04 100644 --- a/src/routes/(console)/organization-[organization]/+page.svelte +++ b/src/routes/(console)/organization-[organization]/+page.svelte @@ -19,6 +19,7 @@ import { trackEvent, Click } from '$lib/actions/analytics'; import { type Models } from '@appwrite.io/console'; import { getServiceLimit, readOnly, upgradeURL } from '$lib/stores/billing'; + import { BillingPlan } from '$lib/constants'; import { onMount, type ComponentType } from 'svelte'; import { canWriteProjects } from '$lib/stores/roles'; import { checkPricingRefAndRedirect } from '$lib/helpers/pricingRedirect'; @@ -162,6 +163,28 @@ {/if} + {#if isCloud && data.organization.billingPlan === BillingPlan.FREE && projectsToArchive.length === 0} + + Your Free plan includes up to 2 projects and limited resources. Upgrade to unlock + more capacity and features. + + + + + {/if} + {#if activeProjects.length > 0} Date: Thu, 18 Sep 2025 19:01:50 +0530 Subject: [PATCH 013/150] added dismiss and cooldown period of 36hrs --- .../organization-[organization]/+page.svelte | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/routes/(console)/organization-[organization]/+page.svelte b/src/routes/(console)/organization-[organization]/+page.svelte index dd58e6e04..db8a39d51 100644 --- a/src/routes/(console)/organization-[organization]/+page.svelte +++ b/src/routes/(console)/organization-[organization]/+page.svelte @@ -20,6 +20,7 @@ import { type Models } from '@appwrite.io/console'; import { getServiceLimit, readOnly, upgradeURL } from '$lib/stores/billing'; import { BillingPlan } from '$lib/constants'; + import { hideNotification, shouldShowNotification } from '$lib/helpers/notifications'; import { onMount, type ComponentType } from 'svelte'; import { canWriteProjects } from '$lib/stores/roles'; import { checkPricingRefAndRedirect } from '$lib/helpers/pricingRedirect'; @@ -46,6 +47,7 @@ let addOrganization = false; let showSelectProject = false; let showCreateProjectCloud = false; + let freePlanAlertDismissed = false; let searchQuery: SearchQuery; @@ -98,7 +100,23 @@ } ]); - onMount(async () => checkPricingRefAndRedirect(page.url.searchParams)); + function dismissFreePlanAlert() { + freePlanAlertDismissed = true; + const notificationId = `freePlanAlert_${data.organization.$id}`; + hideNotification(notificationId, { coolOffPeriod: 36 }); + + trackEvent(Click.OrganizationClickUpgrade, { + from: 'button', + source: 'free_plan_info_alert_dismiss' + }); + } + + onMount(async () => { + checkPricingRefAndRedirect(page.url.searchParams); + const notificationId = `freePlanAlert_${data.organization.$id}`; + const shouldShow = shouldShowNotification(notificationId); + freePlanAlertDismissed = !shouldShow; + }); function findRegion(project: Models.Project) { return $regionsStore.regions.find((region) => region.$id === project.region); @@ -163,8 +181,8 @@ {/if} - {#if isCloud && data.organization.billingPlan === BillingPlan.FREE && projectsToArchive.length === 0} - + {#if isCloud && data.organization.billingPlan === BillingPlan.FREE && projectsToArchive.length === 0 && !freePlanAlertDismissed} + Your Free plan includes up to 2 projects and limited resources. Upgrade to unlock more capacity and features. From 745a225f2100af01fb973252698a5c5c19d6315d Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Fri, 19 Sep 2025 11:25:15 +0530 Subject: [PATCH 014/150] Update +page.svelte --- src/routes/(console)/organization-[organization]/+page.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/(console)/organization-[organization]/+page.svelte b/src/routes/(console)/organization-[organization]/+page.svelte index db8a39d51..a876015d5 100644 --- a/src/routes/(console)/organization-[organization]/+page.svelte +++ b/src/routes/(console)/organization-[organization]/+page.svelte @@ -103,7 +103,7 @@ function dismissFreePlanAlert() { freePlanAlertDismissed = true; const notificationId = `freePlanAlert_${data.organization.$id}`; - hideNotification(notificationId, { coolOffPeriod: 36 }); + hideNotification(notificationId, { coolOffPeriod: 24 }); trackEvent(Click.OrganizationClickUpgrade, { from: 'button', From bbc857d9b74ae7310048076ef90661b965334a98 Mon Sep 17 00:00:00 2001 From: ItzNotABug Date: Sat, 20 Sep 2025 14:42:19 +0530 Subject: [PATCH 015/150] fix: safely move primary key at index 0. --- src/lib/stores/preferences.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/lib/stores/preferences.ts b/src/lib/stores/preferences.ts index fdcb89f7e..723721410 100644 --- a/src/lib/stores/preferences.ts +++ b/src/lib/stores/preferences.ts @@ -66,17 +66,20 @@ function safePrefsKey(widthPreferences: TeamPreferences['widths'], from: string, } } -function safePrefsKeyForOrder( - orderPreferences: TeamPreferences['order'], - from: string, - to: string -) { - if (orderPreferences.includes(from)) { - const index = orderPreferences.indexOf(from); +function safePrefsKeyForOrder(order: TeamPreferences['order'], from: string, to: string) { + if (order.includes(from)) { + const index = order.indexOf(from); if (index !== -1) { - orderPreferences[index] = to; + order[index] = to; } } + + // move to first since its primary! + const toIndex = order.indexOf(to); + if (toIndex !== -1 && toIndex !== 0) { + order.splice(toIndex, 1); + order.unshift(to); + } } // rare cases where the value was an array, probably due to PHP backend. From 085bdc555ddda51f70e842a6416c22aac190c6f3 Mon Sep 17 00:00:00 2001 From: ItzNotABug Date: Sat, 20 Sep 2025 15:13:57 +0530 Subject: [PATCH 016/150] update: optimize ID logic by batching and using a binary search logic. --- src/lib/components/id.svelte | 72 ++++++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 20 deletions(-) diff --git a/src/lib/components/id.svelte b/src/lib/components/id.svelte index c756ee8f6..5ddce54ef 100644 --- a/src/lib/components/id.svelte +++ b/src/lib/components/id.svelte @@ -1,41 +1,73 @@ @@ -338,28 +366,6 @@ } &.thinking { - &::before { - content: ''; - position: absolute; - top: -2px; - left: -2px; - right: -2px; - bottom: -2px; - border-radius: inherit; - padding: 2px; - background: linear-gradient( - 90deg, - rgba(253, 54, 110, 0.02), - rgba(254, 149, 103, 0.05), - rgba(253, 54, 110, 0.02), - rgba(254, 149, 103, 0.05), - rgba(253, 54, 110, 0.02) - ); - background-size: 400% 100%; - animation: border-shimmer 4s ease-in-out infinite; - z-index: -1; - } - &::after { content: ''; position: absolute; @@ -373,15 +379,16 @@ rgba(255, 255, 255, 0.08), transparent ); - animation: inner-shimmer 4s cubic-bezier(0.25, 0.46, 0.45, 0.94) infinite; - z-index: 1; + animation: inner-shimmer 2s cubic-bezier(0.25, 0.46, 0.45, 0.94) infinite; } } } & .floating-action-wrapper :global(:first-child) { - left: 45%; /* change this value if the firstColumn is changed for overlay logic.*/ z-index: 21; + left: calc( + 50% - 40px + ); /* change this value if the firstColumn is changed for overlay logic.*/ } & :global(.spreadsheet-container) { @@ -421,18 +428,6 @@ ); } - @keyframes border-shimmer { - 0% { - background-position: 400% 0; - } - 50% { - background-position: -50% 0; - } - 100% { - background-position: -400% 0; - } - } - @keyframes inner-shimmer { 0% { left: -100%; diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/icon/ai.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/icon/ai.svelte similarity index 100% rename from src/routes/(console)/project-[region]-[project]/databases/database-[database]/icon/ai.svelte rename to src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/icon/ai.svelte diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/index.ts b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/index.ts new file mode 100644 index 000000000..6a0f6f7d8 --- /dev/null +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/index.ts @@ -0,0 +1,3 @@ +export * from './store'; +export { default as Empty } from './empty.svelte'; +export { default as Input } from './input.svelte'; diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/suggestions.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/input.svelte similarity index 92% rename from src/routes/(console)/project-[region]-[project]/databases/database-[database]/suggestions.svelte rename to src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/input.svelte index 0baab1891..83acf0377 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/suggestions.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/input.svelte @@ -4,11 +4,6 @@ import { tableColumnSuggestions } from './store'; import { InputTextarea } from '$lib/elements/forms'; import { Card, Layout, Selector, Typography } from '@appwrite.io/pink-svelte'; - - $effect(() => { - // TODO: update later, this is for mock only atm! - $tableColumnSuggestions.thinking = $tableColumnSuggestions.enabled; - }); diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/store.ts b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/store.ts new file mode 100644 index 000000000..cb042cf76 --- /dev/null +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/store.ts @@ -0,0 +1,13 @@ +import { writable } from 'svelte/store'; + +export type TableColumnSuggestions = { + enabled: boolean; + thinking: boolean; + context?: string | null; +}; + +export const tableColumnSuggestions = writable({ + enabled: false, + context: null, + thinking: false +}); diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/createTable.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/createTable.svelte index e6d7dd08e..f354cbcce 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/createTable.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/createTable.svelte @@ -9,7 +9,7 @@ import { ID } from '@appwrite.io/console'; import { createEventDispatcher } from 'svelte'; import { subNavigation } from '$lib/stores/database'; - import Suggestions from './suggestions.svelte'; + import { Input as SuggestionsInput } from './(suggestions)/index'; let { showCreate = $bindable(false) @@ -105,7 +105,7 @@ } }} /> - + diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/store.ts b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/store.ts index c06d8be46..36e755d77 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/store.ts +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/store.ts @@ -4,21 +4,9 @@ import type { Models } from '@appwrite.io/console'; import { derived, writable } from 'svelte/store'; import { IconChartBar, IconCloudUpload, IconCog } from '@appwrite.io/pink-icons-svelte'; -export type TableColumnSuggestions = { - enabled: boolean; - thinking: boolean; - context?: string | null; -}; - export const database = derived(page, ($page) => $page.data.database as Models.Database); export const showCreateTable = writable(false); -export const tableColumnSuggestions = writable({ - enabled: false, - context: null, - thinking: false -}); - export const tableViewColumns = writable([ { id: '$id', title: 'Table ID', type: 'string', width: 200 }, { id: 'name', title: 'Name', type: 'string', width: { min: 120 } }, diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte index f52a0eb0a..e62eda0d5 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte @@ -29,10 +29,9 @@ import { IconChevronDown, IconChevronUp, IconPlus } from '@appwrite.io/pink-icons-svelte'; import type { Models } from '@appwrite.io/console'; import EmptySheet from './layout/emptySheet.svelte'; - import SuggestionsEmptySheet from './layout/suggestionsEmptySheet.svelte'; import CreateRow from './rows/create.svelte'; import { onDestroy } from 'svelte'; - import { tableColumnSuggestions } from '../store'; + import { Empty as SuggestionsEmptySheet, tableColumnSuggestions } from '../(suggestions)'; export let data: PageData; @@ -218,7 +217,7 @@ }} /> {/if} {:else if $tableColumnSuggestions.thinking} - + {:else} Date: Mon, 22 Sep 2025 10:44:45 +0530 Subject: [PATCH 019/150] lint. --- .../databases/database-[database]/(suggestions)/empty.svelte | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte index 49b3b6291..cf28ca0ea 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte @@ -24,7 +24,9 @@ const { onColumnsFinalized = undefined }: { - onColumnsFinalized?: (columns: Exclude[]) => Promise; + onColumnsFinalized?: ( + columns: Exclude[] + ) => Promise; } = $props(); /** From b961a54a91775b6d362efe6287c63ed1ee128cd1 Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Mon, 22 Sep 2025 12:24:23 +0530 Subject: [PATCH 020/150] fix: Add real-time duration updates for Executions and fix negative timer display --- src/lib/actions/timer.ts | 11 +++++++---- .../function-[function]/executions/sheet.svelte | 7 ++++++- .../function-[function]/executions/table.svelte | 7 ++++++- .../sites/site-[site]/logs/sheet.svelte | 8 +++++++- .../sites/site-[site]/logs/table.svelte | 7 ++++++- 5 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/lib/actions/timer.ts b/src/lib/actions/timer.ts index 1f6bff7e1..07d589a8b 100644 --- a/src/lib/actions/timer.ts +++ b/src/lib/actions/timer.ts @@ -21,13 +21,16 @@ export const timer: Action = (node, props) => { } function step() { - const diffInSeconds = Math.floor((new Date().getTime() - startDate.getTime()) / 1000); - const minutes = Math.floor(diffInSeconds / 60); + const elapsedSeconds = Math.max( + 0, + Math.floor((new Date().getTime() - startDate.getTime()) / 1000) + ); + const minutes = Math.floor(elapsedSeconds / 60); if (minutes > 0) { - const seconds = diffInSeconds % 60; + const seconds = elapsedSeconds % 60; node.textContent = `${minutes}m ${seconds}s`; } else { - node.textContent = calculateTime(diffInSeconds); + node.textContent = calculateTime(elapsedSeconds); } frame = window.requestAnimationFrame(step); } diff --git a/src/routes/(console)/project-[region]-[project]/functions/function-[function]/executions/sheet.svelte b/src/routes/(console)/project-[region]-[project]/functions/function-[function]/executions/sheet.svelte index af47ba07c..48a313d48 100644 --- a/src/routes/(console)/project-[region]-[project]/functions/function-[function]/executions/sheet.svelte +++ b/src/routes/(console)/project-[region]-[project]/functions/function-[function]/executions/sheet.svelte @@ -21,6 +21,7 @@ import { Copy } from '$lib/components'; import { logStatusConverter } from './store'; import { LogsRequest, LogsResponse } from '$lib/components/logs'; + import { timer } from '$lib/actions/timer'; export let selectedLogId: string; export let logs: Models.Execution[]; @@ -139,7 +140,11 @@ Duration - {calculateTime(selectedLog.duration)} + {#if ['processing', 'waiting'].includes(selectedLog.status)} + + {:else} + {calculateTime(selectedLog.duration)} + {/if}
diff --git a/src/routes/(console)/project-[region]-[project]/functions/function-[function]/executions/table.svelte b/src/routes/(console)/project-[region]-[project]/functions/function-[function]/executions/table.svelte index bc4573006..b73c0a7f6 100644 --- a/src/routes/(console)/project-[region]-[project]/functions/function-[function]/executions/table.svelte +++ b/src/routes/(console)/project-[region]-[project]/functions/function-[function]/executions/table.svelte @@ -25,6 +25,7 @@ import { invalidate } from '$app/navigation'; import { Dependencies } from '$lib/constants'; import { Button } from '$lib/elements/forms'; + import { timer } from '$lib/actions/timer'; export let columns: Column[]; export let executions: Models.ExecutionList; @@ -121,7 +122,11 @@ {:else if column.id === 'duration'} - {calculateTime(log.duration)} + {#if ['processing', 'waiting'].includes(log.status)} + + {:else} + {calculateTime(log.duration)} + {/if} {/if} {/each} diff --git a/src/routes/(console)/project-[region]-[project]/sites/site-[site]/logs/sheet.svelte b/src/routes/(console)/project-[region]-[project]/sites/site-[site]/logs/sheet.svelte index a0c6f9b81..8adf821c3 100644 --- a/src/routes/(console)/project-[region]-[project]/sites/site-[site]/logs/sheet.svelte +++ b/src/routes/(console)/project-[region]-[project]/sites/site-[site]/logs/sheet.svelte @@ -19,6 +19,7 @@ import { Copy } from '$lib/components'; import { LogsRequest, LogsResponse } from '$lib/components/logs'; import { site } from '../store'; + import { timer } from '$lib/actions/timer'; export let open = false; export let selectedLogId: string; @@ -112,7 +113,12 @@ Duration - {calculateTime(selectedLog.duration)} + {#if ['processing', 'waiting'].includes(selectedLog.status)} + + {:else} + {calculateTime(selectedLog.duration)} + {/if}
diff --git a/src/routes/(console)/project-[region]-[project]/sites/site-[site]/logs/table.svelte b/src/routes/(console)/project-[region]-[project]/sites/site-[site]/logs/table.svelte index 1d5011dd2..9d74a095d 100644 --- a/src/routes/(console)/project-[region]-[project]/sites/site-[site]/logs/table.svelte +++ b/src/routes/(console)/project-[region]-[project]/sites/site-[site]/logs/table.svelte @@ -14,6 +14,7 @@ import { calculateTime } from '$lib/helpers/timeConversion'; import { getBadgeTypeFromStatusCode } from '$lib/helpers/httpStatus'; import { Button } from '$lib/elements/forms'; + import { timer } from '$lib/actions/timer'; export let columns: Column[]; export let logs: Models.ExecutionList; @@ -83,7 +84,11 @@ {log.requestMethod} {:else if column.id === 'duration'} - {calculateTime(log.duration)} + {#if ['processing', 'waiting'].includes(log.status)} + + {:else} + {calculateTime(log.duration)} + {/if} {:else if column.id === 'responseStatusCode'}
Date: Mon, 22 Sep 2025 13:42:30 +0530 Subject: [PATCH 021/150] fix duration filter --- src/lib/components/filters/quickFilters.ts | 6 ++++++ src/lib/components/filters/setFilters.ts | 16 ++++------------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/lib/components/filters/quickFilters.ts b/src/lib/components/filters/quickFilters.ts index dce8e823d..1632b109b 100644 --- a/src/lib/components/filters/quickFilters.ts +++ b/src/lib/components/filters/quickFilters.ts @@ -49,6 +49,8 @@ export function addFilterAndApply( addStatusCodeFilter(value, colId, columns); } else if (colId === '$createdAt' || colId === '$updatedAt' || colId === 'buildDuration') { addDateFilter(value, colId, columns); + } else if (colId === 'duration') { + addDurationFilter(value, colId, columns); } else { addFilter(columns, colId, operator, value, arrayValues); } @@ -75,3 +77,7 @@ export function addDateFilter(value: string, colId: string, columns: Column[]) { export function addSizeFilter(value: string, colId: string, columns: Column[]) { addFilter(columns, colId, ValidOperators.GreaterThanOrEqual, value); } + +export function addDurationFilter(value: string, colId: string, columns: Column[]) { + addFilter(columns, colId, ValidOperators.GreaterThan, value); +} diff --git a/src/lib/components/filters/setFilters.ts b/src/lib/components/filters/setFilters.ts index b7e0f18c3..e46edc98e 100644 --- a/src/lib/components/filters/setFilters.ts +++ b/src/lib/components/filters/setFilters.ts @@ -61,20 +61,12 @@ export function setTimeFilter(filter: FilterData, columns: Column[]) { const col = columns.find((c) => c.id === filter.id); const timeTag = get(tags).find((tag) => tag.tag.includes(`**${filter.title}**`)); if (timeTag) { - const now = new Date(); - - const diff = now.getTime() - new Date(timeTag.value as string).getTime(); const ranges = col.elements as { value: string; label: string }[]; - const dateRange = ranges.reduce((prev, curr) => { - if (parseInt(curr.value) < diff && curr.value > prev.value) { - return curr; - } - return prev; - }); - if (dateRange) { + const timeRange = ranges.find((range) => range.value === timeTag.value); + if (timeRange) { const newTag = { - tag: `**${filter.title}** is **${dateRange.label}**`, - value: timeTag.value + tag: `**${filter.title}** is **${timeRange.label}**`, + value: timeRange.value }; cleanOldTags(filter?.title); From 2583b7bd49d75bc59cdd88178680ed64a92cccbc Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Mon, 22 Sep 2025 14:03:23 +0530 Subject: [PATCH 022/150] fix: correct spreadsheetRenderKey update in CSV import completion --- src/lib/components/csvImportBox.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/csvImportBox.svelte b/src/lib/components/csvImportBox.svelte index 8ff2838ab..9643afdb1 100644 --- a/src/lib/components/csvImportBox.svelte +++ b/src/lib/components/csvImportBox.svelte @@ -62,7 +62,7 @@ if (isSuccess) { await invalidate(Dependencies.ROWS); - $spreadsheetRenderKey = hash(Date.now().toString()); + spreadsheetRenderKey.set(hash(Date.now().toString())); } } From a4fa521fb20bce264a00d55e030ae60454c54e0e Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Mon, 22 Sep 2025 14:38:36 +0530 Subject: [PATCH 023/150] fix: drop stale items --- src/lib/components/csvImportBox.svelte | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/lib/components/csvImportBox.svelte b/src/lib/components/csvImportBox.svelte index 9643afdb1..0716e5990 100644 --- a/src/lib/components/csvImportBox.svelte +++ b/src/lib/components/csvImportBox.svelte @@ -75,6 +75,7 @@ const current = $importItems.get(importData.$id); let tableName = current?.table ?? null; + let resourceMissing = false; if (!tableName && tableId) { try { @@ -87,9 +88,19 @@ tableName = table.name; } catch { tableName = null; + resourceMissing = true; } } + if (resourceMissing) { + importItems.update((items) => { + const next = new Map(items); + next.delete(importData.$id); + return next; + }); + return; + } + importItems.update((items) => { const existing = items.get(importData.$id); From 8254191cc9161e1a151b5f18ab22f5f107badf6c Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Mon, 22 Sep 2025 15:46:02 +0530 Subject: [PATCH 024/150] remove resourcemissing and using tableName is null --- src/lib/components/csvImportBox.svelte | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/lib/components/csvImportBox.svelte b/src/lib/components/csvImportBox.svelte index 0716e5990..15b5b1e96 100644 --- a/src/lib/components/csvImportBox.svelte +++ b/src/lib/components/csvImportBox.svelte @@ -75,7 +75,6 @@ const current = $importItems.get(importData.$id); let tableName = current?.table ?? null; - let resourceMissing = false; if (!tableName && tableId) { try { @@ -88,11 +87,10 @@ tableName = table.name; } catch { tableName = null; - resourceMissing = true; } } - if (resourceMissing) { + if (tableName === null) { importItems.update((items) => { const next = new Map(items); next.delete(importData.$id); From 1d134715296d54fbda4e4a0195b5646b0ad070c4 Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Mon, 22 Sep 2025 15:49:02 +0530 Subject: [PATCH 025/150] used table id aswell as safe check --- src/lib/components/csvImportBox.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/csvImportBox.svelte b/src/lib/components/csvImportBox.svelte index 15b5b1e96..ec7a2c9a3 100644 --- a/src/lib/components/csvImportBox.svelte +++ b/src/lib/components/csvImportBox.svelte @@ -90,7 +90,7 @@ } } - if (tableName === null) { + if (tableId && tableName === null) { importItems.update((items) => { const next = new Map(items); next.delete(importData.$id); From 2ce54049d8ae7b0a605ae81384bb1aabbfc6aadb Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Mon, 22 Sep 2025 15:54:29 +0530 Subject: [PATCH 026/150] remove table id --- src/lib/components/csvImportBox.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/csvImportBox.svelte b/src/lib/components/csvImportBox.svelte index ec7a2c9a3..15b5b1e96 100644 --- a/src/lib/components/csvImportBox.svelte +++ b/src/lib/components/csvImportBox.svelte @@ -90,7 +90,7 @@ } } - if (tableId && tableName === null) { + if (tableName === null) { importItems.update((items) => { const next = new Map(items); next.delete(importData.$id); From e50ee41714e2f6807db888106496e0d505d0efa4 Mon Sep 17 00:00:00 2001 From: Harsh Mahajan <127186841+HarshMN2345@users.noreply.github.com> Date: Mon, 22 Sep 2025 15:58:15 +0530 Subject: [PATCH 027/150] Update src/lib/components/csvImportBox.svelte Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- src/lib/components/csvImportBox.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/csvImportBox.svelte b/src/lib/components/csvImportBox.svelte index 15b5b1e96..ec7a2c9a3 100644 --- a/src/lib/components/csvImportBox.svelte +++ b/src/lib/components/csvImportBox.svelte @@ -90,7 +90,7 @@ } } - if (tableName === null) { + if (tableId && tableName === null) { importItems.update((items) => { const next = new Map(items); next.delete(importData.$id); From 60d993a532c0db9297f89cfe10811ade3cf4bc7e Mon Sep 17 00:00:00 2001 From: Darshan Date: Mon, 22 Sep 2025 18:32:59 +0530 Subject: [PATCH 028/150] add: column suggestions. --- src/lib/actions/analytics.ts | 3 + src/lib/stores/sdk.ts | 3 +- .../(suggestions)/empty.svelte | 52 ++- .../(suggestions)/index.ts | 1 + .../(suggestions)/review.svelte | 348 ++++++++++++++++++ .../(suggestions)/store.ts | 56 ++- .../database-[database]/+layout.svelte | 18 +- .../database-[database]/createTable.svelte | 58 +-- .../table-[table]/+layout.svelte | 2 + .../table-[table]/+page.svelte | 22 +- .../table-[table]/columns/+page.svelte | 1 - .../table-[table]/createColumn.svelte | 3 +- 12 files changed, 508 insertions(+), 59 deletions(-) create mode 100644 src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/review.svelte diff --git a/src/lib/actions/analytics.ts b/src/lib/actions/analytics.ts index 1f4722965..b511e59c8 100644 --- a/src/lib/actions/analytics.ts +++ b/src/lib/actions/analytics.ts @@ -274,9 +274,12 @@ export enum Submit { DatabaseDelete = 'submit_database_delete', DatabaseUpdateName = 'submit_database_update_name', DatabaseImportCsv = 'submit_database_import_csv', + ColumnCreate = 'submit_column_create', ColumnUpdate = 'submit_column_update', ColumnDelete = 'submit_column_delete', + ColumnSuggestions = 'submit_column_suggestions', + RowCreate = 'submit_row_create', RowDelete = 'submit_row_delete', RowUpdate = 'submit_row_update', diff --git a/src/lib/stores/sdk.ts b/src/lib/stores/sdk.ts index 20ec5b83c..3ea68fa62 100644 --- a/src/lib/stores/sdk.ts +++ b/src/lib/stores/sdk.ts @@ -126,7 +126,8 @@ const sdkForProject = { proxy: new Proxy(clientProject), migrations: new Migrations(clientProject), sites: new Sites(clientProject), - tablesDB: new TablesDB(clientProject) + tablesDB: new TablesDB(clientProject), + console: new Console(clientProject) // for suggestions API }; export const realtime = { diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte index cf28ca0ea..e25d3105b 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte @@ -12,21 +12,25 @@ import { isSmallViewport } from '$lib/stores/viewport'; import { SortButton } from '$lib/components'; import type { Column } from '$lib/helpers/types'; - import { type Columns, expandTabs } from '../table-[table]/store'; + import { expandTabs } from '../table-[table]/store'; import SpreadsheetContainer from '../table-[table]/layout/spreadsheet.svelte'; import { onDestroy, onMount } from 'svelte'; import { debounce } from '$lib/helpers/debounce'; import { sdk } from '$lib/stores/sdk'; import { page } from '$app/state'; - import { tableColumnSuggestions } from './store'; - import type { Models } from '@appwrite.io/console'; + import { + type ColumnInput, + mapSuggestedColumns, + type SuggestedColumnSchema, + tableColumnSuggestions + } from './store'; + import { addNotification } from '$lib/stores/notifications'; + import { Submit, trackError, trackEvent } from '$lib/actions/analytics'; const { onColumnsFinalized = undefined }: { - onColumnsFinalized?: ( - columns: Exclude[] - ) => Promise; + onColumnsFinalized?: (columns: SuggestedColumnSchema[]) => void | Promise; } = $props(); /** @@ -216,29 +220,37 @@ }); async function suggestColumns() { - console.log('tableColumnSuggestions', $tableColumnSuggestions.enabled); - - if (!$tableColumnSuggestions.enabled) return; - $tableColumnSuggestions.thinking = true; try { - const suggestedColumns = await sdk - .forConsoleIn(page.params.repository) + const suggestedColumns = (await sdk + .forProject(page.params.region, page.params.project) .console.suggestColumns({ databaseId: page.params.database, tableId: page.params.table, - context: $tableColumnSuggestions.context - }); + context: $tableColumnSuggestions.context ?? undefined + })) as unknown as { + total: number; + columns: ColumnInput[]; + }; - console.log(JSON.stringify(suggestedColumns, null, 2)); + trackEvent(Submit.ColumnSuggestions, { + total: suggestedColumns.total, + tableName: $tableColumnSuggestions.table?.name ?? undefined + }); - await onColumnsFinalized?.(suggestedColumns.columns); - } catch (e) { - // `null` means - // we couldn't make columns! - await onColumnsFinalized?.(null); + await onColumnsFinalized(mapSuggestedColumns(suggestedColumns.columns)); + } catch (error) { + addNotification({ + type: 'error', + message: error.message + }); + + trackError(error, Submit.ColumnSuggestions); } finally { + $tableColumnSuggestions.table = null; + $tableColumnSuggestions.context = null; + $tableColumnSuggestions.enabled = false; $tableColumnSuggestions.thinking = false; } } diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/index.ts b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/index.ts index 6a0f6f7d8..80654e522 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/index.ts +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/index.ts @@ -1,3 +1,4 @@ export * from './store'; export { default as Empty } from './empty.svelte'; export { default as Input } from './input.svelte'; +export { default as Review } from './review.svelte'; diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/review.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/review.svelte new file mode 100644 index 000000000..e95462724 --- /dev/null +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/review.svelte @@ -0,0 +1,348 @@ + + + + + Review and edit suggested columns before applying + + + + {#each draft as column, index (index)} + + + + { + draft[index] = normalizeColumnType(event.detail, draft[index]); + draft = [...draft]; + }} /> + + + + + {#if index !== draft.length - 1} + + {/if} + {/each} + + + + + + + + + + + + + + + + { + resetDraft(); + show = false; + columns = null; + }}> + Are you sure you want to dismiss these columns suggested by AI? This action is irreversible. + diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/store.ts b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/store.ts index cb042cf76..67bd268ba 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/store.ts +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/store.ts @@ -3,11 +3,63 @@ import { writable } from 'svelte/store'; export type TableColumnSuggestions = { enabled: boolean; thinking: boolean; - context?: string | null; + context?: string | undefined; + + /* for safety when in tables page */ + table: { + id: string; + name: string; + }; +}; + +export type SuggestedColumnSchema = { + key: string; + type: string; + required: boolean; + default?: string | number | boolean | number[] | number[][] | number[][][] | null; + size?: number; + min?: number; + max?: number; + format?: string | null; }; export const tableColumnSuggestions = writable({ enabled: false, context: null, - thinking: false + thinking: false, + table: null }); + +export type ColumnInput = { + name: string; + type: string; + required?: boolean; + default?: string | number | boolean | number[] | number[][] | number[][][] | null; + size?: number; + min?: number; + max?: number; + format?: string; + formatOptions?: { + min?: number; + max?: number; + }; +}; + +export function mapSuggestedColumns(columns: T[]): SuggestedColumnSchema[] { + return columns.map((col) => ({ + key: col.name, + type: col.type, + required: col.required ?? false, + default: col.default ?? null, + size: col.type === 'string' ? (col.size ?? undefined) : undefined, + min: + col.type === 'integer' || col.type === 'double' + ? (col.min ?? col.formatOptions?.min ?? undefined) + : undefined, + max: + col.type === 'integer' || col.type === 'double' + ? (col.max ?? col.formatOptions?.max ?? undefined) + : undefined, + format: col.format ?? null + })); +} diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/+layout.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/+layout.svelte index c4b66e957..dc39232b0 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/+layout.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/+layout.svelte @@ -10,7 +10,6 @@ } from '$lib/commandCenter'; import { tablesSearcher } from '$lib/commandCenter/searchers'; import { Dependencies } from '$lib/constants'; - import type { Models } from '@appwrite.io/console'; import CreateTable from './createTable.svelte'; import { showCreateTable } from './store'; import { TablesPanel } from '$lib/commandCenter/panels'; @@ -24,14 +23,6 @@ const project = page.params.project; const databaseId = page.params.database; - async function handleCreate(event: CustomEvent) { - $showCreateTable = false; - await invalidate(Dependencies.DATABASE); - await goto( - `${base}/project-${page.params.region}-${project}/databases/database-${databaseId}/table-${event.detail.$id}` - ); - } - $: $registerCommands([ { label: 'Create table', @@ -152,4 +143,11 @@ - + { + await invalidate(Dependencies.DATABASE); + await goto( + `${base}/project-${page.params.region}-${project}/databases/database-${databaseId}/table-${table.$id}` + ); + }} /> diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/createTable.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/createTable.svelte index f354cbcce..0f545f890 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/createTable.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/createTable.svelte @@ -6,26 +6,28 @@ import { Button, InputText } from '$lib/elements/forms'; import { addNotification } from '$lib/stores/notifications'; import { sdk } from '$lib/stores/sdk'; - import { ID } from '@appwrite.io/console'; - import { createEventDispatcher } from 'svelte'; + import { ID, type Models } from '@appwrite.io/console'; import { subNavigation } from '$lib/stores/database'; - import { Input as SuggestionsInput } from './(suggestions)/index'; + import { Input as SuggestionsInput, tableColumnSuggestions } from './(suggestions)/index'; let { - showCreate = $bindable(false) + showCreate = $bindable(false), + onTableCreated }: { showCreate: boolean; + onTableCreated: (table: Models.Table) => void | Promise; } = $props(); const databaseId = page.params.database; - const dispatch = createEventDispatcher(); let name = $state(''); let id: string = $state(null); let touchedId = $state(false); let error: string = $state(null); - const create = async () => { + let creatingTable = $state(false); + + async function createTable() { error = null; try { const table = await sdk @@ -36,23 +38,35 @@ name }); - showCreate = false; - subNavigation.update(); + $tableColumnSuggestions.table = { + id: table.$id, + name: table.name + }; - dispatch('created', table); - addNotification({ - type: 'success', - message: `${name} has been created` - }); - name = id = null; - trackEvent(Submit.TableCreate, { - customId: !!id - }); + updateAndCleanup(); + + await onTableCreated(table); + + showCreate = false; + creatingTable = false; } catch (e) { error = e.message; trackError(e, Submit.TableCreate); } - }; + } + + function updateAndCleanup() { + subNavigation.update(); + + addNotification({ + type: 'success', + message: `${name} has been created` + }); + + trackEvent(Submit.TableCreate, { customId: !!id }); + + name = id = null; + } function toIdFormat(str: string): string { return str @@ -79,7 +93,7 @@ }); - + - - + + diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+layout.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+layout.svelte index cc755911b..fcc995b71 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+layout.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+layout.svelte @@ -62,6 +62,7 @@ import { preferences } from '$lib/stores/preferences'; import { buildRowUrl, isRelationship } from './rows/store'; import { chunks } from '$lib/helpers/array'; + import { Submit, trackEvent } from '$lib/actions/analytics'; let editRow: EditRow; let editRelatedRow: EditRelatedRow; @@ -256,6 +257,7 @@ columns = await generateColumns($project, page.params.database, page.params.table); await invalidate(Dependencies.TABLE); + trackEvent(Submit.ColumnCreate, { type: 'faker' }); } catch (e) { addNotification({ type: 'error', diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte index e62eda0d5..f625e72a7 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte @@ -31,12 +31,20 @@ import EmptySheet from './layout/emptySheet.svelte'; import CreateRow from './rows/create.svelte'; import { onDestroy } from 'svelte'; - import { Empty as SuggestionsEmptySheet, tableColumnSuggestions } from '../(suggestions)'; + import { + Review as ReviewColumns, + Empty as SuggestionsEmptySheet, + type SuggestedColumnSchema, + tableColumnSuggestions + } from '../(suggestions)'; export let data: PageData; let showImportCSV = false; + let showSuggestionsModal = false; + let columnSuggestionsSchema: SuggestedColumnSchema[] = []; + // todo: might need a type fix here. const filterColumns = writable([]); @@ -216,8 +224,12 @@ } }} /> {/if} - {:else if $tableColumnSuggestions.thinking} - + {:else if $tableColumnSuggestions.enabled && $tableColumnSuggestions.table && $tableColumnSuggestions.table.id === page.params.table} + { + showSuggestionsModal = true; + columnSuggestionsSchema = columns; + }} /> {:else} +{#if showSuggestionsModal && columnSuggestionsSchema} + +{/if} + From b294c553fd329322b1a61f434b58fe54fc7b46cf Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 24 Sep 2025 15:25:31 +0530 Subject: [PATCH 039/150] update: ui; fix: clicks not working on mobile. --- .../project-[region]-[project]/+layout.svelte | 2 +- .../databases/+page.svelte | 35 ++++++--------- .../(suggestions)/empty.svelte | 43 ++++++++++--------- .../(suggestions)/options.svelte | 11 ++++- .../storage/+page.svelte | 39 +++++++---------- .../storage/bucket-[bucket]/+page.svelte | 5 ++- 6 files changed, 66 insertions(+), 69 deletions(-) diff --git a/src/routes/(console)/project-[region]-[project]/+layout.svelte b/src/routes/(console)/project-[region]-[project]/+layout.svelte index b48424eef..a7679879d 100644 --- a/src/routes/(console)/project-[region]-[project]/+layout.svelte +++ b/src/routes/(console)/project-[region]-[project]/+layout.svelte @@ -138,7 +138,7 @@ @media (max-width: 768px) { .layout-level-progress-bars { - position: relative; + width: 100%; align-items: center; } } diff --git a/src/routes/(console)/project-[region]-[project]/databases/+page.svelte b/src/routes/(console)/project-[region]-[project]/databases/+page.svelte index 67681bddc..093653103 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/+page.svelte @@ -2,9 +2,9 @@ import { goto } from '$app/navigation'; import { base } from '$app/paths'; import { page } from '$app/state'; - import { Empty, PaginationWithLimit, SearchQuery, ViewSelector } from '$lib/components'; + import { Empty, PaginationWithLimit } from '$lib/components'; import { Button } from '$lib/elements/forms'; - import { Container } from '$lib/layout'; + import { Container, ResponsiveContainerHeader } from '$lib/layout'; import type { Models } from '@appwrite.io/console'; import type { PageData } from './$types'; @@ -46,25 +46,18 @@ - - - - - - - {#if $canWriteDatabases} - - {/if} - - + + {#if $canWriteDatabases} + + {/if} + {#if data.databases.total} {#if data.view === 'grid'} diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte index 49deeb8e0..bcbeacf20 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte @@ -11,7 +11,7 @@ Popover } from '@appwrite.io/pink-svelte'; import { IconCalendar, IconFingerPrint, IconPlus } from '@appwrite.io/pink-icons-svelte'; - import { isSmallViewport } from '$lib/stores/viewport'; + import { isSmallViewport, isTabletViewport } from '$lib/stores/viewport'; import type { Column } from '$lib/helpers/types'; import { expandTabs } from '../table-[table]/store'; import SpreadsheetContainer from '../table-[table]/layout/spreadsheet.svelte'; @@ -334,7 +334,7 @@ trackError(error, Submit.ColumnSuggestions); } finally { - $tableColumnSuggestions.table = null; + // $tableColumnSuggestions.table = null; $tableColumnSuggestions.context = null; // $tableColumnSuggestions.enabled = false; $tableColumnSuggestions.thinking = false; @@ -612,8 +612,7 @@ + on:contextmenu={toggle}> -
- - - {#if columnIcon} - - {/if} - - -
+ {#if !$isTabletViewport} +
+ + + {#if columnIcon} + + {/if} + + +
+ {/if}
{/snippet} diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/options.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/options.svelte index 9af78bc93..094aa6840 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/options.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/options.svelte @@ -1,14 +1,17 @@ @@ -16,7 +19,13 @@ {onShowStateChanged?.(showing)} - {@render children(toggle)} + {#if toggleOnTapClick && $isSmallViewport} + + {:else} + {@render children(toggle)} + {/if}
{@render tooltipChildren(toggle)} diff --git a/src/routes/(console)/project-[region]-[project]/storage/+page.svelte b/src/routes/(console)/project-[region]-[project]/storage/+page.svelte index d53830b6f..b9c29bc76 100644 --- a/src/routes/(console)/project-[region]-[project]/storage/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/storage/+page.svelte @@ -4,20 +4,19 @@ - - - - - - - {#if $canWriteBuckets} - - {/if} - - + + {#if $canWriteBuckets} + + {/if} + + {#if data.buckets.total} {#if data.view === 'grid'} diff --git a/src/routes/(console)/project-[region]-[project]/storage/bucket-[bucket]/+page.svelte b/src/routes/(console)/project-[region]-[project]/storage/bucket-[bucket]/+page.svelte index 2d8524fc4..198daff57 100644 --- a/src/routes/(console)/project-[region]-[project]/storage/bucket-[bucket]/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/storage/bucket-[bucket]/+page.svelte @@ -33,6 +33,7 @@ IconPlus, IconTrash } from '@appwrite.io/pink-icons-svelte'; + import { isSmallViewport } from '$lib/stores/viewport'; export let data; @@ -60,7 +61,7 @@ async function fileDeleted(event: CustomEvent) { showDelete = false; - uploader.removeFile(event.detail); + await uploader.removeFile(event.detail); await invalidate(Dependencies.FILES); } @@ -150,7 +151,7 @@ allowSelection bind:selectedRows={selectedFiles} columns={[ - { id: 'filename' }, + { id: 'filename', width: $isSmallViewport ? 24 : undefined }, { id: 'type', width: { min: 140 } }, { id: 'size', width: { min: 100 } }, { id: 'created', width: { min: 120 } }, From e62e58a14906ead6f07afd72d47aad1b3045b4fd Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 24 Sep 2025 15:32:56 +0530 Subject: [PATCH 040/150] fic: notification spacing on phone. --- src/lib/layout/notifications.svelte | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/layout/notifications.svelte b/src/lib/layout/notifications.svelte index ef072472b..5464edb31 100644 --- a/src/lib/layout/notifications.svelte +++ b/src/lib/layout/notifications.svelte @@ -1,7 +1,7 @@ {#if $notifications} @@ -28,16 +28,16 @@ From 3e9480a3faee0df50aace58e515f869751beb04b Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 24 Sep 2025 18:39:10 +0530 Subject: [PATCH 041/150] fic: notification spacing on phone. --- .../project-[region]-[project]/+layout.svelte | 5 + .../(suggestions)/empty.svelte | 260 ++++++++++-------- .../(suggestions)/options.svelte | 41 ++- .../table-[table]/layout/sidesheet.svelte | 2 +- 4 files changed, 189 insertions(+), 119 deletions(-) diff --git a/src/routes/(console)/project-[region]-[project]/+layout.svelte b/src/routes/(console)/project-[region]-[project]/+layout.svelte index a7679879d..f233956c0 100644 --- a/src/routes/(console)/project-[region]-[project]/+layout.svelte +++ b/src/routes/(console)/project-[region]-[project]/+layout.svelte @@ -140,6 +140,11 @@ .layout-level-progress-bars { width: 100%; align-items: center; + box-sizing: border-box; + } + + :global(main:has([data-side-sheet-visible='true']) .layout-level-progress-bars) { + visibility: hidden; } } diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte index bcbeacf20..d1c79f638 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte @@ -103,7 +103,6 @@ // hook the actual horizontal scroller once if (!hScroller || !hScroller.isConnected) { - // TODO: @itznotabug, might not need this. check later. const previousScrollLeft = hScroller?.scrollLeft || 0; hScroller = findHorizontalScroller(headerElement); if (hScroller) { @@ -205,6 +204,32 @@ spreadsheetContainer.style.setProperty('--group-width', `${width + 2}px`); }; + // only for mobile, we can remove if not needed! + const scrollToFirstCustomColumn = () => { + if (!$isSmallViewport || customColumns.length === 0) return; + + if (!headerElement || !headerElement.isConnected) { + headerElement = spreadsheetContainer.querySelector('[role="rowheader"]'); + } + + if (!headerElement) return; + + const firstCustomColumnCell = headerElement.querySelector( + `[role="cell"][data-header="true"][data-column-id="${customColumns[0]?.key}"]` + ); + + if (firstCustomColumnCell && hScroller) { + const cellRect = firstCustomColumnCell.getBoundingClientRect(); + const scrollerRect = hScroller.getBoundingClientRect(); + const scrollLeft = hScroller.scrollLeft + cellRect.left - scrollerRect.left - 40; + + hScroller.scrollTo({ + left: Math.max(0, scrollLeft), + behavior: 'smooth' + }); + } + }; + const recalcAll = () => { updateOverlayHeight(); updateOverlayBounds(); @@ -301,7 +326,7 @@ $tableColumnSuggestions.thinking = true; try { - await sleep(5000); + await sleep(1250); const suggestedColumns = isDev ? mockSuggestions : ((await sdk @@ -325,6 +350,8 @@ // Set hasTransitioned to disable future animations after initial state change if (customColumns.length > 0) { setTimeout(() => (hasTransitioned = true), 300); // After transition completes + + setTimeout(() => scrollToFirstCustomColumn(), 100); } } catch (error) { addNotification({ @@ -343,6 +370,10 @@ function onPopoverShowStateChanged(value: boolean) { showFloatingBar = !value; + if ($isSmallViewport && rangeOverlayEl) { + rangeOverlayEl.style.opacity = value ? '0' : '1'; + } + const currentScrollLeft = hScroller?.scrollLeft || 0; tick().then(() => { @@ -502,6 +533,10 @@ cancelAnimationFrame(scrollAnimationFrame); } }); + + function isCustomColumn(id: string) { + return !['$id', '$createdAt', '$updatedAt', 'actions'].includes(id); + } @@ -520,67 +555,6 @@ class:thinking={$tableColumnSuggestions.thinking} class:no-transition={hasTransitioned && customColumns.length > 0}>
- - {#if $tableColumnSuggestions.thinking} -
- - - - - - Thinking of column suggestions - - - - - { - $tableColumnSuggestions.context = null; - $tableColumnSuggestions.enabled = false; - $tableColumnSuggestions.thinking = false; - }} - >Cancel - - - -
- {:else if customColumns.length > 0 && showFloatingBar} -
- - - {#if creatingColumns} - - {/if} - - - {creatingColumns - ? 'Creating columns...' - : $isSmallViewport - ? 'Review and edit columns' - : 'Review and edit suggested columns before applying'} - - - - - {#if !creatingColumns} - - (confirmDismiss = true)} - >Dismiss - - Apply - - - {/if} - - -
- {/if}
@@ -592,6 +566,7 @@ bottomActionClick={() => {}}> {#each spreadsheetColumns as column, index (index)} + {@const isColumnInteractable = isCustomColumn(column.id)} {#if column.isAction} @@ -607,12 +582,18 @@ ? '--non-overlay-icon-color' : '--overlay-icon-color'} - + {#snippet children(toggle)} + on:contextmenu={(event) => { + if (isColumnInteractable) { + toggle(event); + } + }}> + disabled={!isColumnInteractable} + on:click={(event) => { + if (isColumnInteractable) { + toggle(event); + } + }}> - - - + + - { - const newOption = columnOptions.find( - (opt) => opt.name === e.detail - ); - if (newOption) { - updateColumn(column.id, { - type: newOption.type, - format: newOption.format || null - }); - } - }} - options={basicColumnOptions.map((col) => { - return { - label: col.name, - value: col.name, - leadingIcon: col.icon - }; - })} /> - + { + const newOption = columnOptions.find( + (opt) => opt.name === e.detail + ); + if (newOption) { + updateColumn(column.id, { + type: newOption.type, + format: newOption.format || null + }); + } + }} + options={basicColumnOptions.map((col) => { + return { + label: col.name, + value: col.name, + leadingIcon: col.icon + }; + })} /> + {#if ColumnComponent} {/if} @@ -749,8 +735,70 @@
+ style="height: var(--overlay-height);" + style:opacity={showFloatingBar ? '1' : '0'}>
+ + {#if $tableColumnSuggestions.thinking} +
+ + + + + + Thinking of column suggestions + + + + + { + $tableColumnSuggestions.context = null; + $tableColumnSuggestions.enabled = false; + $tableColumnSuggestions.thinking = false; + }} + >Cancel + + + +
+ {:else if customColumns.length > 0 && showFloatingBar} +
+ + + {#if creatingColumns} + + {/if} + + + {creatingColumns + ? 'Creating columns...' + : $isSmallViewport + ? 'Review and edit columns' + : 'Review and edit suggested columns before applying'} + + + + + {#if !creatingColumns} + + (confirmDismiss = true)} + >Dismiss + + Apply + + + {/if} + + +
+ {/if} void]>; tooltipChildren: Snippet<[toggle: (event: Event) => void]>; - toggleOnTapClick: boolean; + toggleOnTapClick?: boolean; onShowStateChanged?: (showing: boolean) => void; + enabled?: boolean; } = $props(); + + let showSheet = $state(false); + + $effect(() => { + if (!$isSmallViewport) { + showSheet = false; + } + }); - - {onShowStateChanged?.(showing)} + + {onShowStateChanged?.(showing || showSheet)} {#if toggleOnTapClick && $isSmallViewport} - {:else} - {@render children(toggle)} + {/if} -
+
{@render tooltipChildren(toggle)}
+ +{#if $isSmallViewport} + { + showSheet = false; + } + }}> + {@render tooltipChildren(() => (showSheet = false))} + +{/if} diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/layout/sidesheet.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/layout/sidesheet.svelte index 816c51d65..68b981d3a 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/layout/sidesheet.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/layout/sidesheet.svelte @@ -46,7 +46,7 @@ let copyText = $state(undefined); -
+
From e25a0fac9f39cffb5e79bcefee189d3635f8ba1f Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 24 Sep 2025 19:16:46 +0530 Subject: [PATCH 042/150] fix: better fade on show/hide of overlays on mobile. --- .../(suggestions)/empty.svelte | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte index d1c79f638..ff15cef94 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte @@ -47,6 +47,7 @@ let hScroller: HTMLElement | null = null; let headerElement: HTMLElement | null = null; let rangeOverlayEl: HTMLDivElement | null = null; + let fadeBottomOverlayEl: HTMLDivElement | null = null; let customColumns = $state([]); let showFloatingBar = $state(true); @@ -370,8 +371,14 @@ function onPopoverShowStateChanged(value: boolean) { showFloatingBar = !value; - if ($isSmallViewport && rangeOverlayEl) { - rangeOverlayEl.style.opacity = value ? '0' : '1'; + if ($isSmallViewport) { + setTimeout(() => { + [rangeOverlayEl, fadeBottomOverlayEl].forEach((el) => { + if (el) { + el.style.opacity = value ? '0' : '1'; + } + }); + }, 0); } const currentScrollLeft = hScroller?.scrollLeft || 0; @@ -733,14 +740,13 @@
+ data-collapsed-tabs={!$expandTabs}>
{#if $tableColumnSuggestions.thinking} -
+
@@ -841,7 +847,7 @@ transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); &.no-transition { - transition: none !important; + transition: opacity 300ms ease-in-out; } /* pretty gradient wash (with fallback) */ @@ -889,6 +895,10 @@ &.expanded :global(:first-child) { max-width: 525px !important; + + @media (max-width: 768px) { + max-width: 400px !important; + } } } @@ -914,6 +924,7 @@ bottom: 0; width: 100%; position: fixed; + height: var(--overlay-height); background: linear-gradient( 180deg, rgba(255, 255, 255, 0) 0%, @@ -923,7 +934,10 @@ z-index: 20; /* under overlay */ display: flex; justify-content: center; - transition: height 300ms cubic-bezier(0.4, 0, 0.2, 1); + transition: + opacity 300ms ease-in-out, + height 300ms cubic-bezier(0.4, 0, 0.2, 1); + pointer-events: none; } From a192cb5375d5336a096d68d33ec2ed176e2858f0 Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 24 Sep 2025 20:10:45 +0530 Subject: [PATCH 043/150] update: flow. --- package.json | 4 +- pnpm-lock.yaml | 20 +- .../databases/+page.svelte | 2 +- .../(suggestions)/empty.svelte | 134 ++++--- .../(suggestions)/index.ts | 1 - .../(suggestions)/review.svelte | 353 ------------------ .../table-[table]/+page.svelte | 14 +- 7 files changed, 92 insertions(+), 436 deletions(-) delete mode 100644 src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/review.svelte diff --git a/package.json b/package.json index f14b99ed0..eebc54597 100644 --- a/package.json +++ b/package.json @@ -24,9 +24,9 @@ "@ai-sdk/svelte": "^1.1.24", "@appwrite.io/console": "https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@f08cb74", "@appwrite.io/pink-icons": "0.25.0", - "@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@4e5cc18", + "@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@077179c", "@appwrite.io/pink-legacy": "^1.0.3", - "@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@4e5cc18", + "@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@077179c", "@faker-js/faker": "^9.9.0", "@popperjs/core": "^2.11.8", "@sentry/sveltekit": "^8.38.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 78a74c254..3a4161ed8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18,14 +18,14 @@ importers: specifier: 0.25.0 version: 0.25.0 '@appwrite.io/pink-icons-svelte': - specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@4e5cc18 - version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@4e5cc18(svelte@5.25.3) + specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@077179c + version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@077179c(svelte@5.25.3) '@appwrite.io/pink-legacy': specifier: ^1.0.3 version: 1.0.3 '@appwrite.io/pink-svelte': - specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@4e5cc18 - version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@4e5cc18(svelte@5.25.3) + specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@077179c + version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@077179c(svelte@5.25.3) '@faker-js/faker': specifier: ^9.9.0 version: 9.9.0 @@ -269,8 +269,8 @@ packages: peerDependencies: svelte: ^4.0.0 - '@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@4e5cc18': - resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@4e5cc18} + '@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@077179c': + resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@077179c} version: 2.0.0-RC.1 peerDependencies: svelte: ^4.0.0 @@ -284,8 +284,8 @@ packages: '@appwrite.io/pink-legacy@1.0.3': resolution: {integrity: sha512-GGde5fmPhs+s6/3aFeMPc/kKADG/gTFkYQSy6oBN8pK0y0XNCLrZZgBv+EBbdhwdtqVEWXa0X85Mv9w7jcIlwQ==} - '@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@4e5cc18': - resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@4e5cc18} + '@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@077179c': + resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@077179c} version: 2.0.0-RC.2 peerDependencies: svelte: ^4.0.0 @@ -3709,7 +3709,7 @@ snapshots: dependencies: svelte: 5.25.3 - '@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@4e5cc18(svelte@5.25.3)': + '@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@077179c(svelte@5.25.3)': dependencies: svelte: 5.25.3 @@ -3722,7 +3722,7 @@ snapshots: '@appwrite.io/pink-icons': 1.0.0 the-new-css-reset: 1.11.3 - '@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@4e5cc18(svelte@5.25.3)': + '@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@077179c(svelte@5.25.3)': dependencies: '@appwrite.io/pink-icons-svelte': 2.0.0-RC.1(svelte@5.25.3) '@floating-ui/dom': 1.6.13 diff --git a/src/routes/(console)/project-[region]-[project]/databases/+page.svelte b/src/routes/(console)/project-[region]-[project]/databases/+page.svelte index 093653103..e95c01cca 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/+page.svelte @@ -14,7 +14,7 @@ import Table from './table.svelte'; import { registerCommands } from '$lib/commandCenter'; import { canWriteDatabases } from '$lib/stores/roles'; - import { Icon, Layout } from '@appwrite.io/pink-svelte'; + import { Icon } from '@appwrite.io/pink-svelte'; import { IconPlus } from '@appwrite.io/pink-icons-svelte'; import EmptySearch from '$lib/components/emptySearch.svelte'; diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte index ff15cef94..536ec7568 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte @@ -21,10 +21,10 @@ import { type ColumnInput, mapSuggestedColumns, - mockSuggestions, type SuggestedColumnSchema, tableColumnSuggestions, - basicColumnOptions + basicColumnOptions, + mockSuggestions } from './store'; import { addNotification } from '$lib/stores/notifications'; import { Submit, trackError, trackEvent } from '$lib/actions/analytics'; @@ -32,15 +32,11 @@ import { invalidate } from '$app/navigation'; import { Dependencies } from '$lib/constants'; import type { Columns } from '../table-[table]/store'; - - import { isDev } from '$lib/system'; import { columnOptions } from '../table-[table]/columns/store'; import Options from './options.svelte'; import { InputSelect, InputText } from '$lib/elements/forms'; import { Confirm } from '$lib/components'; - // No props needed - we handle column creation directly - let resizeObserver: ResizeObserver; let spreadsheetContainer: HTMLElement; @@ -262,7 +258,8 @@ size: col.size, min: col.min, max: col.max, - width: 180, + // TODO: @itznotabug, we should use a dynamic min width based on column's name. + width: { min: 180 }, icon: columnOption?.icon, draggable: false, resizable: false @@ -323,36 +320,62 @@ await suggestColumns(); }); + function resetSuggestionsStore(fullReset: boolean = true) { + if (fullReset) { + // these are referenced in + // `table-[table]/+page.svelte` + $tableColumnSuggestions.table = null; + $tableColumnSuggestions.enabled = false; + } + + $tableColumnSuggestions.context = null; + $tableColumnSuggestions.thinking = false; + } + + /** + * Mark this as `true` when developing locally, + * make sure not to spend credits unnecessarily! + */ + const useMockSuggestions = false; async function suggestColumns() { $tableColumnSuggestions.thinking = true; + let suggestedColumns: { + total: number; + columns: ColumnInput[]; + } = { + total: 0, + columns: [] + }; try { - await sleep(1250); - const suggestedColumns = isDev - ? mockSuggestions - : ((await sdk - .forProject(page.params.region, page.params.project) - .console.suggestColumns({ - databaseId: page.params.database, - tableId: page.params.table, - context: $tableColumnSuggestions.context ?? undefined - })) as unknown as { - total: number; - columns: ColumnInput[]; - }); + if (useMockSuggestions) { + /* animation */ + await sleep(1250); + suggestedColumns = mockSuggestions; + } else { + suggestedColumns = (await sdk + .forProject(page.params.region, page.params.project) + .console.suggestColumns({ + databaseId: page.params.database, + tableId: page.params.table, + context: $tableColumnSuggestions.context ?? undefined + })) as unknown as { + total: number; + columns: ColumnInput[]; + }; + } + const tableName = $tableColumnSuggestions.table?.name ?? undefined; trackEvent(Submit.ColumnSuggestions, { - total: suggestedColumns.total, - tableName: $tableColumnSuggestions.table?.name ?? undefined + tableName, + total: suggestedColumns.total }); customColumns = mapSuggestedColumns(suggestedColumns.columns); - // Set hasTransitioned to disable future animations after initial state change if (customColumns.length > 0) { - setTimeout(() => (hasTransitioned = true), 300); // After transition completes - - setTimeout(() => scrollToFirstCustomColumn(), 100); + setTimeout(scrollToFirstCustomColumn, 100); + setTimeout(() => (hasTransitioned = true), 300); } } catch (error) { addNotification({ @@ -362,10 +385,7 @@ trackError(error, Submit.ColumnSuggestions); } finally { - // $tableColumnSuggestions.table = null; - $tableColumnSuggestions.context = null; - // $tableColumnSuggestions.enabled = false; - $tableColumnSuggestions.thinking = false; + resetSuggestionsStore(false); } } @@ -511,11 +531,6 @@ await invalidate(Dependencies.TABLE); - // Reset state - customColumns = []; - $tableColumnSuggestions.context = null; - $tableColumnSuggestions.enabled = false; - addNotification({ type: 'success', message: 'Columns created successfully' @@ -528,7 +543,6 @@ type: 'error', message: error.message }); - } finally { creatingColumns = false; } } @@ -539,6 +553,9 @@ if (scrollAnimationFrame) { cancelAnimationFrame(scrollAnimationFrame); } + + customColumns = []; + resetSuggestionsStore(); }); function isCustomColumn(id: string) { @@ -559,8 +576,8 @@ aria-hidden="true" bind:this={rangeOverlayEl} class="columns-range-overlay" - class:thinking={$tableColumnSuggestions.thinking} - class:no-transition={hasTransitioned && customColumns.length > 0}> + class:no-transition={hasTransitioned && customColumns.length > 0} + class:thinking={$tableColumnSuggestions.thinking || creatingColumns}>
@@ -595,6 +612,7 @@ {#snippet children(toggle)} { if (isColumnInteractable) { @@ -760,31 +778,32 @@ { - $tableColumnSuggestions.context = null; - $tableColumnSuggestions.enabled = false; - $tableColumnSuggestions.thinking = false; - }} + on:click={() => resetSuggestionsStore()} >Cancel
{:else if customColumns.length > 0 && showFloatingBar} -
+
- {#if creatingColumns} - - {/if} + + {#if creatingColumns} + + {/if} - - {creatingColumns - ? 'Creating columns...' - : $isSmallViewport - ? 'Review and edit columns' - : 'Review and edit suggested columns before applying'} - + + {creatingColumns + ? 'Creating columns...' + : $isSmallViewport + ? 'Review and edit columns' + : 'Review and edit suggested columns before applying'} + + @@ -814,8 +833,7 @@ bind:open={confirmDismiss} onSubmit={() => { customColumns = []; - $tableColumnSuggestions.context = null; - $tableColumnSuggestions.enabled = false; + resetSuggestionsStore(); }}> Are you sure you want to dismiss these columns suggested by AI? This action is irreversible. @@ -900,6 +918,10 @@ max-width: 400px !important; } } + + &.creating-columns :global(:first-child) { + max-width: 300px !important; + } } & :global(.spreadsheet-container) { diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/index.ts b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/index.ts index 80654e522..6a0f6f7d8 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/index.ts +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/index.ts @@ -1,4 +1,3 @@ export * from './store'; export { default as Empty } from './empty.svelte'; export { default as Input } from './input.svelte'; -export { default as Review } from './review.svelte'; diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/review.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/review.svelte deleted file mode 100644 index c2f5193cd..000000000 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/review.svelte +++ /dev/null @@ -1,353 +0,0 @@ - - - - - Review and edit suggested columns before applying - - - - {#each draft as column, index (index)} - - - - { - draft[index] = normalizeColumnType(event.detail, draft[index]); - draft = [...draft]; - }} /> - - - - - {#if index !== draft.length - 1} - - {/if} - {/each} - - - - - - - - - - - - - - - - { - resetDraft(); - show = false; - columns = null; - }}> - Are you sure you want to dismiss these columns suggested by AI? This action is irreversible. - diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte index ca33f1415..b6d825c4f 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte @@ -31,20 +31,12 @@ import EmptySheet from './layout/emptySheet.svelte'; import CreateRow from './rows/create.svelte'; import { onDestroy } from 'svelte'; - import { - Review as ReviewColumns, - Empty as SuggestionsEmptySheet, - type SuggestedColumnSchema, - tableColumnSuggestions - } from '../(suggestions)'; + import { Empty as SuggestionsEmptySheet, tableColumnSuggestions } from '../(suggestions)'; export let data: PageData; let showImportCSV = false; - let showSuggestionsModal = false; - let columnSuggestionsSchema: SuggestedColumnSchema[] = []; - // todo: might need a type fix here. const filterColumns = writable([]); @@ -268,10 +260,6 @@ bind:showSheet={$showRowCreateSheet.show} bind:existingData={$showRowCreateSheet.row} /> -{#if showSuggestionsModal && columnSuggestionsSchema} - -{/if} - From c05e2f5ab91f140b1cd06d0126a325c9c5d7a2fb Mon Sep 17 00:00:00 2001 From: Darshan Date: Fri, 26 Sep 2025 11:04:37 +0530 Subject: [PATCH 064/150] update: cancel navigating when suggestions are up for review. update: change border colors for custom columns. --- src/lib/layout/progress.svelte | 8 ++ src/lib/stores/navigation.ts | 3 + .../(suggestions)/empty.svelte | 105 ++++++++++++++++-- .../(suggestions)/input.svelte | 7 +- .../(suggestions)/store.ts | 5 +- .../database-[database]/subNavigation.svelte | 14 ++- 6 files changed, 129 insertions(+), 13 deletions(-) create mode 100644 src/lib/stores/navigation.ts diff --git a/src/lib/layout/progress.svelte b/src/lib/layout/progress.svelte index 8d34097f4..a5b445fcc 100644 --- a/src/lib/layout/progress.svelte +++ b/src/lib/layout/progress.svelte @@ -1,4 +1,5 @@ diff --git a/src/lib/stores/navigation.ts b/src/lib/stores/navigation.ts new file mode 100644 index 000000000..f35211058 --- /dev/null +++ b/src/lib/stores/navigation.ts @@ -0,0 +1,3 @@ +import { writable } from 'svelte/store'; + +export const navigationCancelled = writable(false); diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte index e8f94c43d..cb76163a0 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte @@ -24,12 +24,16 @@ type SuggestedColumnSchema, tableColumnSuggestions, basicColumnOptions, - mockSuggestions + mockSuggestions, + createTableRequest } from './store'; import { addNotification } from '$lib/stores/notifications'; import { Submit, trackError, trackEvent } from '$lib/actions/analytics'; import { sleep } from '$lib/helpers/promises'; - import { invalidate } from '$app/navigation'; + import { invalidate, beforeNavigate, goto } from '$app/navigation'; + import { showCreateTable } from '../store'; + import { showSubNavigation } from '$lib/stores/layout'; + import { navigationCancelled } from '$lib/stores/navigation'; import { Dependencies } from '$lib/constants'; import { isWithinSafeRange } from '$lib/helpers/numbers'; import type { Columns } from '../table-[table]/store'; @@ -52,6 +56,9 @@ let scrollAnimationFrame: number | null = null; let confirmDismiss = $state(false); + let confirmNavigation = $state(false); + let pendingNavigationUrl: string | null = null; + let creatingColumns = $state(false); const baseColProps = { draggable: false, resizable: false }; @@ -252,6 +259,25 @@ }); }; + // Handle create table requests from subNavigation + const unsubscribeCreateTable = createTableRequest.subscribe((requested) => { + if (requested) { + if (customColumns.length > 0 && !creatingColumns) { + confirmNavigation = true; + pendingNavigationUrl = 'create-table'; + } else { + executeCreateTable(); + } + + createTableRequest.set(false); + } + }); + + function executeCreateTable() { + $showCreateTable = true; + $showSubNavigation = false; + } + const customSuggestedColumns = $derived.by(() => { return customColumns.map((col: SuggestedColumnSchema) => { const columnOption = getColumnOption(col.type, col.format); @@ -314,6 +340,16 @@ } ]; + // Handle browser back/forward navigation + const handleBeforeUnload = (event: BeforeUnloadEvent) => { + if (customColumns.length > 0 && !creatingColumns) { + event.preventDefault(); + event.returnValue = + 'You have unsaved column suggestions. Are you sure you want to leave?'; + return event.returnValue; + } + }; + const spreadsheetColumns = $derived(getRowColumns()); const emptyCells = $derived(($isSmallViewport ? 14 : 17) + (!$expandTabs ? 2 : 0)); @@ -327,7 +363,20 @@ await suggestColumns(); }); + beforeNavigate(({ cancel, to }) => { + if (customColumns.length > 0 && !creatingColumns) { + cancel(); + confirmNavigation = true; + $navigationCancelled = true; + pendingNavigationUrl = to?.url?.pathname || null; + } + }); + function resetSuggestionsStore(fullReset: boolean = true) { + if ($tableColumnSuggestions.table?.id !== page.params.table) { + return; + } + if (fullReset) { // these are referenced in // `table-[table]/+page.svelte` @@ -434,6 +483,10 @@ ); } + function isCustomColumn(id: string) { + return !['$id', '$createdAt', '$updatedAt', 'actions'].includes(id); + } + async function createColumns() { creatingColumns = true; const client = sdk.forProject(page.params.region, page.params.project); @@ -563,14 +616,11 @@ customColumns = []; resetSuggestionsStore(); + unsubscribeCreateTable(); }); - - function isCustomColumn(id: string) { - return !['$id', '$createdAt', '$updatedAt', 'actions'].includes(id); - } - +
+ { + customColumns = []; + resetSuggestionsStore(); + confirmNavigation = false; + + if (pendingNavigationUrl) { + if (pendingNavigationUrl === 'create-table') { + executeCreateTable(); + } else { + goto(pendingNavigationUrl); + } + + pendingNavigationUrl = null; + } + }}> + You have unsaved column suggestions. If you leave this page, you'll lose these suggestions. Are + you sure you want to continue? + + diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/icon/aiNotification.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/icon/aiNotification.svelte new file mode 100644 index 000000000..1fe702004 --- /dev/null +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/icon/aiNotification.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/indexes.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/indexes.svelte new file mode 100644 index 000000000..365e8f05f --- /dev/null +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/indexes.svelte @@ -0,0 +1,293 @@ + + + + + {#await loadIndexSuggestions()} + {#each Array(3) as _, index} + {@const firstItem = index === 0} + + {@render fieldSkeleton({ label: 'Key', showLabel: firstItem })} + {@render fieldSkeleton({ label: 'Type', showLabel: firstItem })} + {@render fieldSkeleton({ label: 'Order', showLabel: firstItem })} + + +
+ +
+
+ {/each} + + + + + {:then suggestedIndexes} + {#each suggestedIndexes as index, count} + {@const firstItem = count === 0} + + + + + + + + + +
+ +
+
+ {/each} + + {#if indexes.length < MAX_INDEXES} + + + + {/if} + {/await} +
+ + + + + + + + + + +
+ +{#snippet fieldSkeleton({ label, showLabel })} + + {#if showLabel} + {label} + {/if} + + +{/snippet} diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/store.ts b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/store.ts index e0432b6f2..b8c022bc4 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/store.ts +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/store.ts @@ -1,4 +1,5 @@ import { writable } from 'svelte/store'; +import { IndexType } from '@appwrite.io/console'; import { columnOptions } from '../table-[table]/columns/store'; export type TableColumnSuggestions = { @@ -24,6 +25,20 @@ export type SuggestedColumnSchema = { format?: string | null; }; +export enum IndexOrder { + ASC = 'ASC', + DESC = 'DESC', + NONE = null +} + +export type SuggestedIndexSchema = { + key: string; + type: IndexType; + orders: IndexOrder; + columns: string[]; + lengths?: number[] | undefined; +}; + export const tableColumnSuggestions = writable({ enabled: false, context: null, @@ -33,6 +48,8 @@ export const tableColumnSuggestions = writable({ export const createTableRequest = writable(false); +export const showIndexesSuggestions = writable(false); + export const mockSuggestions: { total: number; columns: ColumnInput[] } = { total: 7, columns: [ diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+layout.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+layout.svelte index fcc995b71..74d71ee07 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+layout.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+layout.svelte @@ -64,6 +64,8 @@ import { chunks } from '$lib/helpers/array'; import { Submit, trackEvent } from '$lib/actions/analytics'; + import IndexesSuggestions from '../(suggestions)/indexes.svelte'; + let editRow: EditRow; let editRelatedRow: EditRelatedRow; let editRowPermissions: EditRowPermissions; @@ -470,3 +472,5 @@ + + diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/createIndex.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/createIndex.svelte index 85c5b518a..cb19528dc 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/createIndex.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/createIndex.svelte @@ -154,7 +154,7 @@ ] : undefined }); - trackEvent(Submit.IndexCreate); + trackEvent(Submit.IndexCreate, { type: 'manual' }); showCreateIndex = false; } catch (err) { addNotification({ From 8f577e9ecdf6220cc2d9bfedf1d4b4e1c3e685a0 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sat, 27 Sep 2025 12:29:00 +0530 Subject: [PATCH 072/150] update: index suggestions mobile views with accordion. --- .../(suggestions)/indexes.svelte | 409 +++++++++++++----- 1 file changed, 291 insertions(+), 118 deletions(-) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/indexes.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/indexes.svelte index 365e8f05f..9b1face53 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/indexes.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/indexes.svelte @@ -1,5 +1,5 @@ - - - {#await loadIndexSuggestions()} - {#each Array(3) as _, index} - {@const firstItem = index === 0} - - {@render fieldSkeleton({ label: 'Key', showLabel: firstItem })} - {@render fieldSkeleton({ label: 'Type', showLabel: firstItem })} - {@render fieldSkeleton({ label: 'Order', showLabel: firstItem })} - +{#if !$isSmallViewport} + { + await applySuggestedIndexes(); + }}> + + {#await loadIndexSuggestions()} + {#each Array(3) as _, index} + {@const firstItem = index === 0} + + {@render fieldSkeleton({ label: 'Key', showLabel: firstItem })} + {@render fieldSkeleton({ label: 'Type', showLabel: firstItem })} + {@render fieldSkeleton({ label: 'Order', showLabel: firstItem })} + -
- -
-
- {/each} +
+ +
+
+ {/each} - - - - {:then suggestedIndexes} - {#each suggestedIndexes as index, count} - {@const firstItem = count === 0} - - + {@render addIndexButton()} + {/await} + - + + + + - - - - -
- -
-
- {/each} - - {#if indexes.length < MAX_INDEXES} - - - {/if} - {/await} -
- - - - - - - - - -
+ +
+{/if} -{#snippet fieldSkeleton({ label, showLabel })} +{#if $isSmallViewport} + await applySuggestedIndexes() + }}> + {#if modalError} + + {modalError} + + {/if} + + {#await loadIndexSuggestions()} + + {#each Array(3) as _} + {@render fieldSkeleton({ + label: undefined, + showLabel: false, + isDesktop: false + })} + {/each} + + {:then suggestedIndexes} + + {#each suggestedIndexes as index, count} + + {@render indexEditForm({ index, count, isDesktop: false })} + + {/each} + + {@render addIndexButton()} + + {/await} + +{/if} + +{#snippet fieldSkeleton({ label, showLabel, isDesktop = true })} {#if showLabel} {label} {/if} - + {/snippet} + +{#snippet indexEditForm({ index, count, isDesktop = true })} + {@const firstItem = count === 0} + {#if isDesktop} + + + + + + + + {@render removeIndexButton({ count, isDesktop: true })} + + {:else} + + + + + + + + + + + {@render removeIndexButton({ count, isDesktop: false })} + + {/if} +{/snippet} + +{#snippet addIndexButton()} + {#if indexes.length < MAX_INDEXES} + + + + {/if} +{/snippet} + +{#snippet removeIndexButton({ count, isDesktop = true })} + {#if isDesktop} +
+ +
+ {:else if indexes.length > 1} + + + + {/if} +{/snippet} From e1d526c3f62dc3a7a31356be6d8625ede75e41e5 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sat, 27 Sep 2025 12:29:27 +0530 Subject: [PATCH 073/150] update: misc. styles --- .../database-[database]/(suggestions)/empty.svelte | 9 ++++++--- .../database-[database]/(suggestions)/input.svelte | 6 +++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte index 7a2272d95..19c8c72a4 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte @@ -863,9 +863,12 @@ - + Thinking of column suggestions - + @@ -990,7 +993,7 @@ transition: box-shadow 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94); } - & :global(.column-resizer-disabled) { + & :global([role='cell']:not([data-column-id='actions']) .column-resizer-disabled) { border-left: var(--border-width-s, 1px) solid rgba(253, 54, 110, 0.24) !important; transition: border-color 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94); } diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/input.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/input.svelte index b663588e9..bf5f63109 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/input.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/input.svelte @@ -5,6 +5,7 @@ import { InputTextarea } from '$lib/elements/forms'; import { Card, Layout, Selector, Typography } from '@appwrite.io/pink-svelte'; import { onMount } from 'svelte'; + import { isSmallViewport } from '$lib/stores/viewport'; onMount(() => { // enable by default! @@ -14,7 +15,10 @@ - + From c08145d9efdf97ce3f3b4f8c4c31a9e5157af381 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sat, 27 Sep 2025 12:30:29 +0530 Subject: [PATCH 074/150] update: make spreadsheet cols on columns and index sheets as non-resizable. --- .../table-[table]/columns/+page.svelte | 22 ++++++++++++++----- .../table-[table]/indexes/+page.svelte | 8 +++---- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/columns/+page.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/columns/+page.svelte index b767d0cdf..9936f4e41 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/columns/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/columns/+page.svelte @@ -226,9 +226,9 @@ bind:selectedRows={selectedColumns} columns={[ // more size until we decide if we want a new column! - { id: 'key', width: { min: $isSmallViewport ? 250 : 200 } }, - { id: 'indexed', width: { min: 150 } }, - { id: 'default', width: { min: 200 } }, + { id: 'key', width: { min: 300 }, resizable: false }, + { id: 'indexed', width: { min: 150 }, resizable: false }, + { id: 'default', width: { min: 200 }, resizable: false }, { id: 'actions', width: 40, isAction: true } ]} bottomActionClick={() => ($showCreateColumnSheet.show = true)}> @@ -269,13 +269,13 @@ direction="row" alignItems="center" gap="xxs"> - + {#if column.key === '$id' || column.key === '$sequence' || column.key === '$createdAt' || column.key === '$updatedAt'} - {column['name']} + {column.key} {:else} {column.key} {column.array ? '[]' : undefined} {/if} - + {#if isString(column) && column.encrypt} {/if} + + diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte index 53a4327e9..632cefcdf 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/+page.svelte @@ -53,11 +53,11 @@ let showOverview = $state(false); let columns = $state([ - { id: 'key' }, - { id: 'type' }, - { id: 'columns' }, + { id: 'key', width: { min: $isSmallViewport ? 250 : 200 }, resizable: false }, + { id: 'type', width: 120, resizable: false }, + { id: 'columns', width: { min: 200, resizable: false } }, // { id: 'orders' }, // design doesn't have orders atm - { id: 'lengths' }, + { id: 'lengths', width: { min: 180, resizable: false } }, { id: 'actions', width: 40, isAction: true } ]); From be1747179512e7649c95fe431b9936d9c39a86ec Mon Sep 17 00:00:00 2001 From: Darshan Date: Sat, 27 Sep 2025 12:30:58 +0530 Subject: [PATCH 075/150] add: icons to select columns on index creation. --- .../table-[table]/indexes/createIndex.svelte | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/createIndex.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/createIndex.svelte index cb19528dc..ff95b6af5 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/createIndex.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/createIndex.svelte @@ -12,8 +12,9 @@ import { isRelationship, isSpatialType } from '../rows/store'; import { table, indexes } from '../store'; import { Icon, Layout } from '@appwrite.io/pink-svelte'; - import { IconPlus, IconX } from '@appwrite.io/pink-icons-svelte'; + import { IconCalendar, IconFingerPrint, IconPlus, IconX } from '@appwrite.io/pink-icons-svelte'; import { isSmallViewport } from '$lib/stores/viewport'; + import { columnOptions as baseColumnOptions } from '../columns/store'; let { showCreateIndex = $bindable(false), @@ -37,7 +38,11 @@ } return !isRelationship(column) && !isSpatialType(column); // keep non-relationship and non-spatial }) - .map((column) => ({ value: column.key, label: column.key })) + .map((column) => ({ + value: column.key, + label: column.key, + leadingIcon: baseColumnOptions.find((option) => option.type === column.type).icon + })) ); let columnList = $state([{ value: '', order: '', length: null }]); @@ -196,9 +201,17 @@ ...(selectedType === IndexType.Spatial ? [] : [ - { value: '$id', label: '$id' }, - { value: '$createdAt', label: '$createdAt' }, - { value: '$updatedAt', label: '$updatedAt' } + { value: '$id', label: '$id', leadingIcon: IconFingerPrint }, + { + value: '$createdAt', + label: '$createdAt', + leadingIcon: IconCalendar + }, + { + value: '$updatedAt', + label: '$updatedAt', + leadingIcon: IconCalendar + } ]), ...columnOptions ]} From 4bb90aa7f576dc3d82b3b017c55a31df6729b0b9 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sat, 27 Sep 2025 12:32:27 +0530 Subject: [PATCH 076/150] fix: icon in footer shown above floating action bars. --- src/lib/layout/footer.svelte | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib/layout/footer.svelte b/src/lib/layout/footer.svelte index 2e4f9819c..54e8ec053 100644 --- a/src/lib/layout/footer.svelte +++ b/src/lib/layout/footer.svelte @@ -181,6 +181,11 @@ &.hide { display: none; } + + & :global(i) { + // because the `IconCloud` shows above floating action bars. + position: unset; + } } :global(main:has(.sub-navigation)) footer { From f2ff9d61c866ea027abea9a1171d4165c9fe6ff2 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sat, 27 Sep 2025 12:33:34 +0530 Subject: [PATCH 077/150] fix: selection count not updated on indexes deletions. --- .../table-[table]/indexes/deleteIndex.svelte | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/deleteIndex.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/deleteIndex.svelte index 956bb1b6a..5916d398a 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/deleteIndex.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/indexes/deleteIndex.svelte @@ -34,8 +34,6 @@ }) ) ); - await invalidate(Dependencies.TABLE); - showDelete = false; addNotification({ type: 'success', message: @@ -43,7 +41,13 @@ ? 'Index has been deleted' : `${selectedKeys.length} indexes have been deleted` }); + trackEvent(Submit.IndexDelete); + + await invalidate(Dependencies.TABLE); + + showDelete = false; + selectedIndex = Array.isArray(selectedIndex) ? [] : null; } catch (e) { error = e.message; trackError(e, Submit.IndexDelete); From b0403c59628f48cc90eb152199a1235a439d7d2c Mon Sep 17 00:00:00 2001 From: Darshan Date: Sat, 27 Sep 2025 12:43:00 +0530 Subject: [PATCH 078/150] misc: comment update. --- .../database-[database]/(suggestions)/indexes.svelte | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/indexes.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/indexes.svelte index 9b1face53..3be1fa5b7 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/indexes.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/indexes.svelte @@ -144,7 +144,7 @@ // prepare lengths array let lengths: (number | null)[]; if (index.type === IndexType.Key) { - // Only validate if it's a key index + // only validate if it's a key index lengths = index.columns.map((columnKey, i) => { const column = columnMap.get(columnKey); if (column?.type === 'string') { @@ -188,10 +188,10 @@ for (const [_, index] of indexes.entries()) { try { - // Prepare and validate index data + // prepare and validate index data const { orders, lengths } = prepareIndexForCreation(index, columnMap); - // Generate unique key name for the index + // generate unique key name for the index index.key = generateUniqueIndexKey(index, usedKeys); await sdkClient.tablesDB.createIndex({ From 34aaf77b7c23c4e0d7b5bd8318121f639a84a87c Mon Sep 17 00:00:00 2001 From: Darshan Date: Sat, 27 Sep 2025 12:57:22 +0530 Subject: [PATCH 079/150] fix: modal not dismissed and content reloading due to inlined svelte `await`. --- .../(suggestions)/indexes.svelte | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/indexes.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/indexes.svelte index 3be1fa5b7..3e87b117d 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/indexes.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/indexes.svelte @@ -169,6 +169,17 @@ return { orders, lengths }; } + async function closeAndInvalidate() { + // close modal/sheet. + $showIndexesSuggestions = false; + + /** + * Invalidate dependencies after modal/sheet close, + * because otherwise the `await` block will re-run in the modal. + */ + await invalidate(Dependencies.TABLE); + } + async function applySuggestedIndexes(): Promise { modalError = null; creatingIndexes = true; @@ -228,8 +239,7 @@ type: 'success' }); - // invalidate dependencies. - await invalidate(Dependencies.TABLE); + await closeAndInvalidate(); } else if (successCount > 0) { // some succeeded, some failed addNotification({ @@ -237,8 +247,7 @@ type: 'warning' }); - // invalidate dependencies. - await invalidate(Dependencies.TABLE); + await closeAndInvalidate(); } else { // all failed addNotification({ From 23b410cfbcb176fb70185e3208727bf1ab6705e4 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sat, 27 Sep 2025 13:10:04 +0530 Subject: [PATCH 080/150] update: button states when loading the suggestions. --- .../(suggestions)/indexes.svelte | 25 ++++++++++++++----- .../table-[table]/layout/sidesheet.svelte | 17 +++++++++++-- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/indexes.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/indexes.svelte index 3e87b117d..a5f7e72b1 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/indexes.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/indexes.svelte @@ -30,6 +30,7 @@ let modalError = $state(null); let creatingIndexes = $state(false); + let loadingSuggestions = $state(false); let indexes = $state([]); let columnOptions: Array<{ value: string; @@ -61,6 +62,7 @@ async function loadIndexSuggestions(): Promise { modalError = null; + loadingSuggestions = true; if (VARS.MOCK_AI_SUGGESTIONS) { await sleep(1250); @@ -93,6 +95,8 @@ makeColumnOptions(); + loadingSuggestions = false; + return indexes; } @@ -313,14 +317,17 @@ - + @@ -337,9 +344,10 @@ bind:show={$showIndexesSuggestions} submit={{ text: 'Create', - disabled: indexes.length === 0 || creatingIndexes, + disabled: indexes.length === 0 || creatingIndexes || loadingSuggestions, onClick: async () => await applySuggestedIndexes() - }}> + }} + cancel={{ disabled: loadingSuggestions }}> {#if modalError} {modalError} @@ -447,7 +455,12 @@ {#snippet addIndexButton()} {#if indexes.length < MAX_INDEXES} - diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/layout/sidesheet.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/layout/sidesheet.svelte index 68b981d3a..1665e2a42 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/layout/sidesheet.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/layout/sidesheet.svelte @@ -11,6 +11,7 @@ title, closeOnBlur = false, submit, + cancel, children = null, footer = null, titleBadge = null, @@ -36,6 +37,12 @@ onClick?: () => boolean | void | Promise; } | undefined; + cancel?: + | { + text?: string; + disabled?: boolean; + } + | undefined; children?: Snippet; footer?: Snippet | null; } = $props(); @@ -110,8 +117,14 @@ {#if footer} {@render footer?.()} {/if} - + + + + on:click={() => { + if (indexes.length > 0 && !creatingIndexes) { + confirmDismiss = true; + } else { + $showIndexesSuggestions = false; + } + }}>Cancel + on:click={() => { + if (cancel?.onClick) { + cancel.onClick(); + } else { + show = false; + } + }}>{cancel?.text ?? 'Cancel'}
- Enable AI to suggest useful columns based on your table name + {subtitle} - {#if $tableColumnSuggestions.enabled} + {#if !featureActive} + + + + {/if} + + + {#if $tableColumnSuggestions.enabled && featureActive}
diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte index b6d825c4f..4889cfe4d 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte @@ -31,6 +31,7 @@ import EmptySheet from './layout/emptySheet.svelte'; import CreateRow from './rows/create.svelte'; import { onDestroy } from 'svelte'; + import { isCloud } from '$lib/system'; import { Empty as SuggestionsEmptySheet, tableColumnSuggestions } from '../(suggestions)'; export let data: PageData; @@ -75,6 +76,12 @@ $: hasColumns = !!$table.columns.length; $: hasValidColumns = $table?.columns?.some((col) => col.status === 'available'); + $: canShowSuggestionsSheet = + // enabled, has table details + // and it matches current table + $tableColumnSuggestions.enabled && + $tableColumnSuggestions.table && + $tableColumnSuggestions.table.id === page.params.table; async function onSelect(file: Models.File, localFile = false) { $isCsvImportInProgress = true; @@ -216,7 +223,7 @@ } }} /> {/if} - {:else if $tableColumnSuggestions.enabled && $tableColumnSuggestions.table && $tableColumnSuggestions.table.id === page.params.table} + {:else if isCloud && canShowSuggestionsSheet} {:else} Date: Mon, 29 Sep 2025 16:52:35 +0530 Subject: [PATCH 103/150] feat: Add relative time tooltips in tables; fix Messaging Topics empty state --- .../billing/paymentHistory.svelte | 7 +++-- .../domains/+page.svelte | 10 +++++-- .../auth/+page.svelte | 11 +++++-- .../teams/team-[team]/members/+page.svelte | 4 +-- .../auth/user-[user]/identities/table.svelte | 4 +-- .../auth/user-[user]/memberships/+page.svelte | 4 +-- .../auth/user-[user]/targets/table.svelte | 4 +-- .../database-[database]/delete.svelte | 6 ++-- .../database-[database]/table.svelte | 4 +-- .../messaging/+page.svelte | 4 +-- .../message-[message]/updateTopics.svelte | 29 +++++++++++-------- .../messaging/topics/table.svelte | 6 ++-- .../topics/topic-[topic]/table.svelte | 4 +-- .../overview/(components)/table.svelte | 15 ++++++++-- 14 files changed, 70 insertions(+), 42 deletions(-) diff --git a/src/routes/(console)/organization-[organization]/billing/paymentHistory.svelte b/src/routes/(console)/organization-[organization]/billing/paymentHistory.svelte index e6c787b0d..d8085642a 100644 --- a/src/routes/(console)/organization-[organization]/billing/paymentHistory.svelte +++ b/src/routes/(console)/organization-[organization]/billing/paymentHistory.svelte @@ -3,7 +3,7 @@ import { onMount } from 'svelte'; import { CardGrid, PaginationInline } from '$lib/components'; import { Button } from '$lib/elements/forms'; - import { toLocaleDate } from '$lib/helpers/date'; + import DualTimeView from '$lib/components/dualTimeView.svelte'; import { formatCurrency } from '$lib/helpers/numbers'; import type { Invoice, InvoiceList } from '$lib/sdk/billing'; import { getApiEndpoint, sdk } from '$lib/stores/sdk'; @@ -112,8 +112,9 @@ {#each invoiceList?.invoices as invoice (invoice.$id)} {@const status = invoice.status} - {toLocaleDate(invoice.dueAt)} + + + {@const isDanger = status === 'overdue' || diff --git a/src/routes/(console)/organization-[organization]/domains/+page.svelte b/src/routes/(console)/organization-[organization]/domains/+page.svelte index 4e98e3d80..ec6c271e0 100644 --- a/src/routes/(console)/organization-[organization]/domains/+page.svelte +++ b/src/routes/(console)/organization-[organization]/domains/+page.svelte @@ -4,7 +4,7 @@ import { EmptySearch, PaginationWithLimit, ViewSelector } from '$lib/components/index.js'; import { Button } from '$lib/elements/forms'; import Link from '$lib/elements/link.svelte'; - import { toLocaleDateTime } from '$lib/helpers/date'; + import DualTimeView from '$lib/components/dualTimeView.svelte'; import Container from '$lib/layout/container.svelte'; import { protocol } from '$routes/(console)/store.js'; import { @@ -103,9 +103,13 @@ {:else if column.id === 'nameservers'} {domain.nameservers || '-'} {:else if column.id === 'expiry_date'} - {domain?.expire ? toLocaleDateTime(domain.expire) : '-'} + {#if domain?.expire} + + {:else}-{/if} {:else if column.id === 'renewal'} - {domain?.renewal ? toLocaleDateTime(domain.renewal) : '-'} + {#if domain?.renewal} + + {:else}-{/if} {:else if column.id === 'auto_renewal'} {domain?.autoRenewal ? 'On' : 'Off'} {/if} diff --git a/src/routes/(console)/project-[region]-[project]/auth/+page.svelte b/src/routes/(console)/project-[region]-[project]/auth/+page.svelte index 2bc7fb4fa..24b959222 100644 --- a/src/routes/(console)/project-[region]-[project]/auth/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/auth/+page.svelte @@ -15,7 +15,8 @@ SearchQuery } from '$lib/components'; import { Button } from '$lib/elements/forms'; - import { toLocaleDate, toLocaleDateTime } from '$lib/helpers/date'; + import { toLocaleDate } from '$lib/helpers/date'; + import DualTimeView from '$lib/components/dualTimeView.svelte'; import { Container } from '$lib/layout'; import type { Models } from '@appwrite.io/console'; import { writable } from 'svelte/store'; @@ -196,9 +197,13 @@ {user.labels.join(', ')} {:else if id === 'joined'} - {toLocaleDateTime(user.registration)} + {:else if id === 'lastActivity'} - {user.accessedAt ? toLocaleDate(user.accessedAt) : 'never'} + {#if user.accessedAt} + + {:else} + never + {/if} {:else} {user[id]} {/if} diff --git a/src/routes/(console)/project-[region]-[project]/auth/teams/team-[team]/members/+page.svelte b/src/routes/(console)/project-[region]-[project]/auth/teams/team-[team]/members/+page.svelte index 00d33151b..629c5bdda 100644 --- a/src/routes/(console)/project-[region]-[project]/auth/teams/team-[team]/members/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/auth/teams/team-[team]/members/+page.svelte @@ -6,7 +6,7 @@ import type { Models } from '@appwrite.io/console'; import { invalidate } from '$app/navigation'; import { base } from '$app/paths'; - import { toLocaleDateTime } from '$lib/helpers/date'; + import DualTimeView from '$lib/components/dualTimeView.svelte'; import type { PageData } from './$types'; import CreateMember from '../createMembership.svelte'; import DeleteMembership from '../deleteMembership.svelte'; @@ -117,7 +117,7 @@ {membership.roles} - {toLocaleDateTime(membership.joined)} + {/if} From 91728ac5df18288f4ec980b4cfe4511464d9ebaf Mon Sep 17 00:00:00 2001 From: Darshan Date: Tue, 30 Sep 2025 13:06:10 +0530 Subject: [PATCH 118/150] remove: unnecessary stack. --- .../database-[database]/(suggestions)/input.svelte | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/input.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/input.svelte index 842329a9c..31272972c 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/input.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/input.svelte @@ -34,10 +34,8 @@ - - {title} - + {title} {subtitle} From 009c99f33765e8af90fb43f231ec04a66291a442 Mon Sep 17 00:00:00 2001 From: Darshan Date: Tue, 30 Sep 2025 13:11:59 +0530 Subject: [PATCH 119/150] remove: unnecessary stack. --- .../database-[database]/(suggestions)/empty.svelte | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte index 5cfe6564c..f37214cf7 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte @@ -456,10 +456,18 @@ customColumns = customColumns.slice(0, mappedColumns.length); } - // length should match at this point.! + // replace existing placeholders and + // add any additional columns if needed mappedColumns.forEach((column, index) => { setTimeout(() => { - customColumns[index] = { ...column, isPlaceholder: false }; + if (index < customColumns.length) { + // replace existing placeholder + customColumns[index] = { ...column, isPlaceholder: false }; + } else { + // new column directly if we have more than expected + // just added in case the max ever changes on backend! + customColumns.push({ ...column, isPlaceholder: false }); + } // recalculate overlay bounds // after each column is populated! From a145b612ff41fdcbd9564ccaf599b296ab6d2a54 Mon Sep 17 00:00:00 2001 From: Darshan Date: Tue, 30 Sep 2025 14:57:11 +0530 Subject: [PATCH 120/150] remove: `$derived` --- src/lib/components/alerts/emailVerificationBanner.svelte | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib/components/alerts/emailVerificationBanner.svelte b/src/lib/components/alerts/emailVerificationBanner.svelte index 3f15955ba..72e2bd016 100644 --- a/src/lib/components/alerts/emailVerificationBanner.svelte +++ b/src/lib/components/alerts/emailVerificationBanner.svelte @@ -12,9 +12,8 @@ const needsEmailVerification = $derived(hasUser && !$user.emailVerification); const notOnOnboarding = $derived(!page.route.id.includes('/onboarding')); const notOnWizard = $derived(!$wizard.show && !$isNewWizardStatusOpen); - const isEnabledViaEnvConfig = $derived(VARS.EMAIL_VERIFICATION); const shouldShowEmailBanner = $derived( - isEnabledViaEnvConfig && + VARS.EMAIL_VERIFICATION && isCloud && hasUser && needsEmailVerification && From 6060c510ccaabb2f9a191ccb720fd97db464eba4 Mon Sep 17 00:00:00 2001 From: Darshan Date: Tue, 30 Sep 2025 22:56:35 +0530 Subject: [PATCH 121/150] update: logic for proper spread of excess widths. --- .../(suggestions)/empty.svelte | 94 +++++++++++-------- 1 file changed, 57 insertions(+), 37 deletions(-) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte index f37214cf7..9431d7b9b 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte @@ -156,10 +156,8 @@ const actionsRect = actionsCell.getBoundingClientRect(); const left = Math.round(idRect.right - containerRect.left); const actionsLeft = actionsRect.left - containerRect.left; - const width = Math.min( - customColumns.length * 180, // estimated minimum placeholder width - actionsLeft - left // don't exceed actions column - ); + + const width = actionsLeft - left; spreadsheetContainer.style.setProperty('--group-left', `${left - 2}px`); spreadsheetContainer.style.setProperty('--group-width', `${width + 2}px`); @@ -219,27 +217,22 @@ const left = Math.round(startLeft - containerRect.left); - // maximum possible width of all custom columns - const totalFullWidth = customColumns.reduce( - (total, col) => total + getColumnWidth(col.key), - 0 - ); - // get the actions column and use its left border as the boundary const actionsCell = headerElement!.querySelector( '[role="cell"][data-column-id="actions"]' ); - const rawVisibleWidth = Math.round(visibleRight - idRect.right); - let maxAllowedWidth = rawVisibleWidth; - if (actionsCell) { - const actionsRect = actionsCell.getBoundingClientRect(); - const actionsLeft = actionsRect.left - containerRect.left; - maxAllowedWidth = Math.min(rawVisibleWidth, actionsLeft - left); + if (!actionsCell) { + if (rangeOverlayEl) { + rangeOverlayEl.style.display = 'none'; + } + return; } - // Set overlay width to not exceed actions column boundary - const width = Math.min(totalFullWidth, maxAllowedWidth); + const actionsRect = actionsCell.getBoundingClientRect(); + const actionsLeft = actionsRect.left - containerRect.left; + + const width = actionsLeft - left; // Apply overlay positioning spreadsheetContainer.style.setProperty('--group-left', `${left - 2}px`); @@ -338,25 +331,52 @@ }); }); - const getRowColumns = (): Column[] => [ - { - id: '$id', - title: '$id', - type: 'string', - width: 180, - icon: IconFingerPrint, - ...baseColProps - }, - ...customSuggestedColumns, - { - id: 'actions', - title: '', - type: 'string' as Column['type'], - width: 40, - isAction: true, - ...baseColProps - } - ]; + const getRowColumns = (): Column[] => { + const minColumnWidth = 180; + const fixedWidths = { id: minColumnWidth, actions: 40, selection: 40 }; + + // calculate base widths and total + const columnsWithBase = customSuggestedColumns.map((col) => ({ + ...col, + baseWidth: Math.max(minColumnWidth, getColumnWidth(col.id)) + })); + + const totalUsed = + fixedWidths.id + + fixedWidths.actions + + fixedWidths.selection + + columnsWithBase.reduce((sum, col) => sum + col.baseWidth, 0); + + // distribute excess space equally across custom columns + const viewportWidth = spreadsheetContainer?.clientWidth || window.innerWidth; + const extraPerColumn = + Math.max(0, viewportWidth - totalUsed) / (columnsWithBase.length || 1); + + const finalCustomColumns = columnsWithBase.map((col) => ({ + ...col, + width: { min: col.baseWidth + extraPerColumn } + })); + + return [ + { + id: '$id', + title: '$id', + type: 'string', + width: fixedWidths.id, + icon: IconFingerPrint, + ...baseColProps + }, + ...finalCustomColumns, + { + id: 'actions', + title: '', + type: 'string' as Column['type'], + width: fixedWidths.actions, + isAction: true, + ...baseColProps + } + ]; + }; // Handle browser back/forward navigation const handleBeforeUnload = (event: BeforeUnloadEvent) => { From 3aa2e2b42006c3df55b2bfe21c507cef85f89288 Mon Sep 17 00:00:00 2001 From: Darshan Date: Tue, 30 Sep 2025 23:04:13 +0530 Subject: [PATCH 122/150] update: safe fallback. --- .../databases/database-[database]/(suggestions)/empty.svelte | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte index 9431d7b9b..100fdb384 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte @@ -348,7 +348,10 @@ columnsWithBase.reduce((sum, col) => sum + col.baseWidth, 0); // distribute excess space equally across custom columns - const viewportWidth = spreadsheetContainer?.clientWidth || window.innerWidth; + const viewportWidth = + spreadsheetContainer?.clientWidth || + (typeof window !== 'undefined' ? window.innerWidth : totalUsed); + const extraPerColumn = Math.max(0, viewportWidth - totalUsed) / (columnsWithBase.length || 1); From 8aff15d5bf6b8dee32cd120e66ec2f7e589b9e8e Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 1 Oct 2025 15:13:01 +0530 Subject: [PATCH 123/150] remove: dismiss confirmation! --- src/lib/layout/progress.svelte | 8 -- src/lib/stores/navigation.ts | 3 - .../(suggestions)/empty.svelte | 83 ++----------------- .../(suggestions)/store.ts | 2 - .../database-[database]/subNavigation.svelte | 12 +-- 5 files changed, 7 insertions(+), 101 deletions(-) delete mode 100644 src/lib/stores/navigation.ts diff --git a/src/lib/layout/progress.svelte b/src/lib/layout/progress.svelte index a5b445fcc..8d34097f4 100644 --- a/src/lib/layout/progress.svelte +++ b/src/lib/layout/progress.svelte @@ -1,5 +1,4 @@ diff --git a/src/lib/stores/navigation.ts b/src/lib/stores/navigation.ts deleted file mode 100644 index f35211058..000000000 --- a/src/lib/stores/navigation.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { writable } from 'svelte/store'; - -export const navigationCancelled = writable(false); diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte index 100fdb384..87c37d0f0 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte @@ -25,23 +25,18 @@ tableColumnSuggestions, basicColumnOptions, mockSuggestions, - createTableRequest, showIndexesSuggestions } from './store'; import { addNotification, dismissNotification } from '$lib/stores/notifications'; import { Submit, trackError, trackEvent } from '$lib/actions/analytics'; import { sleep } from '$lib/helpers/promises'; - import { invalidate, beforeNavigate, goto } from '$app/navigation'; - import { showCreateTable } from '../store'; - import { showSubNavigation } from '$lib/stores/layout'; - import { navigationCancelled } from '$lib/stores/navigation'; + import { invalidate } from '$app/navigation'; import { Dependencies } from '$lib/constants'; import { isWithinSafeRange } from '$lib/helpers/numbers'; import type { Columns } from '../table-[table]/store'; import { columnOptions } from '../table-[table]/columns/store'; import Options from './options.svelte'; import { InputSelect, InputText } from '$lib/elements/forms'; - import { Confirm } from '$lib/components'; import { isCloud, VARS } from '$lib/system'; import IconAINotification from './icon/aiNotification.svelte'; @@ -62,10 +57,6 @@ let hasTransitioned = $state(false); let scrollAnimationFrame: number | null = null; - let confirmDismiss = $state(false); - let confirmNavigation = $state(false); - let pendingNavigationUrl: string | null = null; - let creatingColumns = $state(false); const baseColProps = { draggable: false, resizable: false }; @@ -289,26 +280,6 @@ }); }; - // Handle create table requests from subNavigation - const unsubscribeCreateTable = createTableRequest.subscribe((requested) => { - if (requested) { - const hasRealColumns = customColumns.some((col) => !col.isPlaceholder); - if (hasRealColumns && !creatingColumns) { - confirmNavigation = true; - pendingNavigationUrl = 'create-table'; - } else { - executeCreateTable(); - } - - createTableRequest.set(false); - } - }); - - function executeCreateTable() { - $showCreateTable = true; - $showSubNavigation = false; - } - const customSuggestedColumns = $derived.by(() => { return customColumns.map((col: SuggestedColumnSchema) => { const columnOption = getColumnOption(col.type, col.format); @@ -405,16 +376,6 @@ await suggestColumns(); }); - beforeNavigate(({ cancel, to }) => { - const hasRealColumns = customColumns.some((col) => !col.isPlaceholder); - if (hasRealColumns && !creatingColumns) { - cancel(); - confirmNavigation = true; - $navigationCancelled = true; - pendingNavigationUrl = to?.url?.pathname || null; - } - }); - function resetSuggestionsStore(fullReset: boolean = true) { if ($tableColumnSuggestions.table?.id !== page.params.table) { return; @@ -740,7 +701,6 @@ customColumns = []; resetSuggestionsStore(); - unsubscribeCreateTable(); }); @@ -1023,7 +983,10 @@ size="xs" variant="text" disabled={creatingColumns} - on:click={() => (confirmDismiss = true)} + on:click={() => { + customColumns = []; + resetSuggestionsStore(); + }} style="opacity: {creatingColumns ? '0' : '1'}" >Dismiss @@ -1042,42 +1005,6 @@ {/if}
- { - customColumns = []; - resetSuggestionsStore(); - }}> - Are you sure you want to dismiss these columns suggested by AI? This action is irreversible. - - - { - customColumns = []; - resetSuggestionsStore(); - confirmNavigation = false; - - if (pendingNavigationUrl) { - if (pendingNavigationUrl === 'create-table') { - executeCreateTable(); - } else { - goto(pendingNavigationUrl); - } - - pendingNavigationUrl = null; - } - }}> - You have unsaved column suggestions. If you leave this page, you'll lose these suggestions. Are - you sure you want to continue? - -