diff --git a/.env.example b/.env.example index 9ba4011c07..30973b2c1e 100644 --- a/.env.example +++ b/.env.example @@ -3,4 +3,6 @@ PUBLIC_CONSOLE_FEATURE_FLAGS= PUBLIC_APPWRITE_MULTI_REGION=false PUBLIC_APPWRITE_ENDPOINT=http://localhost/v1 PUBLIC_STRIPE_KEY= -PUBLIC_GROWTH_ENDPOINT= \ No newline at end of file +PUBLIC_GROWTH_ENDPOINT= +PUBLIC_CONSOLE_EMAIL_VERIFICATION=false +PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS=true \ No newline at end of file diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 8cfa0c03fa..71bb39ae7d 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -41,6 +41,7 @@ jobs: "PUBLIC_CONSOLE_MODE=cloud" "PUBLIC_CONSOLE_FEATURE_FLAGS=" "PUBLIC_APPWRITE_MULTI_REGION=true" + "PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS=false" "PUBLIC_GROWTH_ENDPOINT=${{ secrets.PUBLIC_GROWTH_ENDPOINT }}" "PUBLIC_STRIPE_KEY=${{ secrets.PUBLIC_STRIPE_KEY }}" "SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}" @@ -81,6 +82,7 @@ jobs: "PUBLIC_CONSOLE_MODE=cloud" "PUBLIC_CONSOLE_FEATURE_FLAGS=" "PUBLIC_APPWRITE_MULTI_REGION=true" + "PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS=false" "PUBLIC_GROWTH_ENDPOINT=${{ secrets.PUBLIC_GROWTH_ENDPOINT }}" "PUBLIC_STRIPE_KEY=${{ secrets.PUBLIC_STRIPE_KEY_STAGE }}" publish-self-hosted: @@ -118,6 +120,7 @@ jobs: build-args: | "PUBLIC_CONSOLE_MODE=self-hosted" "PUBLIC_APPWRITE_MULTI_REGION=false" + "PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS=false" "PUBLIC_CONSOLE_FEATURE_FLAGS=" "PUBLIC_GROWTH_ENDPOINT=${{ secrets.PUBLIC_GROWTH_ENDPOINT }}" @@ -156,6 +159,7 @@ jobs: build-args: | "PUBLIC_CONSOLE_MODE=cloud" "PUBLIC_APPWRITE_MULTI_REGION=false" + "PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS=false" "PUBLIC_CONSOLE_FEATURE_FLAGS=" "PUBLIC_STRIPE_KEY=${{ secrets.PUBLIC_STRIPE_KEY_STAGE }}" "PUBLIC_GROWTH_ENDPOINT=${{ secrets.PUBLIC_GROWTH_ENDPOINT }}" diff --git a/Dockerfile b/Dockerfile index e172247b67..879a4bc65f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,6 +23,7 @@ ADD ./static /app/static ARG PUBLIC_CONSOLE_MODE ARG PUBLIC_CONSOLE_FEATURE_FLAGS ARG PUBLIC_APPWRITE_MULTI_REGION +ARG PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS ARG PUBLIC_APPWRITE_ENDPOINT ARG PUBLIC_GROWTH_ENDPOINT ARG PUBLIC_STRIPE_KEY @@ -34,6 +35,7 @@ ENV PUBLIC_GROWTH_ENDPOINT=$PUBLIC_GROWTH_ENDPOINT ENV PUBLIC_CONSOLE_MODE=$PUBLIC_CONSOLE_MODE ENV PUBLIC_CONSOLE_FEATURE_FLAGS=$PUBLIC_CONSOLE_FEATURE_FLAGS ENV PUBLIC_APPWRITE_MULTI_REGION=$PUBLIC_APPWRITE_MULTI_REGION +ENV PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS=$PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS ENV PUBLIC_STRIPE_KEY=$PUBLIC_STRIPE_KEY ENV SENTRY_AUTH_TOKEN=$SENTRY_AUTH_TOKEN ENV SENTRY_RELEASE=$SENTRY_RELEASE diff --git a/package.json b/package.json index 69a51845ea..eebc545976 100644 --- a/package.json +++ b/package.json @@ -22,11 +22,11 @@ }, "dependencies": { "@ai-sdk/svelte": "^1.1.24", - "@appwrite.io/console": "https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@7747562", + "@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@f2198f1", + "@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@f2198f1", + "@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 826d1cce51..3a4161ed87 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,20 +12,20 @@ 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@7747562 - version: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@7747562 + specifier: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@f08cb74 + version: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@f08cb74 '@appwrite.io/pink-icons': specifier: 0.25.0 version: 0.25.0 '@appwrite.io/pink-icons-svelte': - specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@f2198f1 - version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@f2198f1(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@f2198f1 - version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@f2198f1(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 @@ -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@7747562': - resolution: {tarball: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@7747562} + '@appwrite.io/console@https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@f08cb74': + resolution: {tarball: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@f08cb74} version: 1.10.0 '@appwrite.io/pink-icons-svelte@2.0.0-RC.1': @@ -269,8 +269,8 @@ packages: peerDependencies: svelte: ^4.0.0 - '@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@f2198f1': - resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@f2198f1} + '@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@f2198f1': - resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@f2198f1} + '@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 @@ -3703,13 +3703,13 @@ snapshots: '@analytics/type-utils@0.6.2': {} - '@appwrite.io/console@https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@7747562': {} + '@appwrite.io/console@https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@f08cb74': {} '@appwrite.io/pink-icons-svelte@2.0.0-RC.1(svelte@5.25.3)': dependencies: svelte: 5.25.3 - '@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@f2198f1(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@f2198f1(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/lib/actions/analytics.ts b/src/lib/actions/analytics.ts index 1f47229654..b511e59c8d 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/components/alerts/emailVerificationBanner.svelte b/src/lib/components/alerts/emailVerificationBanner.svelte index 9af46d4189..96574d8016 100644 --- a/src/lib/components/alerts/emailVerificationBanner.svelte +++ b/src/lib/components/alerts/emailVerificationBanner.svelte @@ -6,14 +6,20 @@ import SendVerificationEmailModal from '../account/sendVerificationEmailModal.svelte'; import { page } from '$app/stores'; import { wizard, isNewWizardStatusOpen } from '$lib/stores/wizard'; - import { isCloud } from '$lib/system'; + import { isCloud, VARS } from '$lib/system'; const hasUser = $derived(!!$user); 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( - isCloud && hasUser && needsEmailVerification && notOnOnboarding && notOnWizard + isEnabledViaEnvConfig && + isCloud && + hasUser && + needsEmailVerification && + notOnOnboarding && + notOnWizard ); let showSendVerification = $state(false); diff --git a/src/lib/layout/notifications.svelte b/src/lib/layout/notifications.svelte index ef072472bf..5464edb317 100644 --- a/src/lib/layout/notifications.svelte +++ b/src/lib/layout/notifications.svelte @@ -1,7 +1,7 @@ {#if $notifications} @@ -28,16 +28,16 @@ diff --git a/src/lib/layout/progress.svelte b/src/lib/layout/progress.svelte index 8d34097f4b..a5b445fcc3 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 0000000000..f35211058d --- /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/lib/stores/sdk.ts b/src/lib/stores/sdk.ts index 20ec5b83cc..3ea68fa620 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/lib/system.ts b/src/lib/system.ts index 0e34088726..4532609925 100644 --- a/src/lib/system.ts +++ b/src/lib/system.ts @@ -10,7 +10,9 @@ export const VARS = { CONSOLE_MODE: (env.PUBLIC_CONSOLE_MODE as Mode) ?? undefined, APPWRITE_ENDPOINT: env.PUBLIC_APPWRITE_ENDPOINT ?? undefined, GROWTH_ENDPOINT: env.PUBLIC_GROWTH_ENDPOINT ?? undefined, - PUBLIC_STRIPE_KEY: env.PUBLIC_STRIPE_KEY ?? undefined + PUBLIC_STRIPE_KEY: env.PUBLIC_STRIPE_KEY ?? undefined, + EMAIL_VERIFICATION: env.PUBLIC_CONSOLE_EMAIL_VERIFICATION === 'true', + MOCK_AI_SUGGESTIONS: (env.PUBLIC_CONSOLE_MOCK_AI_SUGGESTIONS ?? 'true') === 'true' }; export const ENV = { diff --git a/src/routes/(console)/project-[region]-[project]/+layout.svelte b/src/routes/(console)/project-[region]-[project]/+layout.svelte index b48424eeff..f233956c07 100644 --- a/src/routes/(console)/project-[region]-[project]/+layout.svelte +++ b/src/routes/(console)/project-[region]-[project]/+layout.svelte @@ -138,8 +138,13 @@ @media (max-width: 768px) { .layout-level-progress-bars { - position: relative; + 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/+page.svelte b/src/routes/(console)/project-[region]-[project]/databases/+page.svelte index 67681bddcb..e95c01ccae 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'; @@ -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'; @@ -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 new file mode 100644 index 0000000000..38ee34a9a0 --- /dev/null +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte @@ -0,0 +1,1160 @@ + + + + +
0} + class="databases-spreadsheet spreadsheet-container-outer" + style:--overlay-icon-color="#fd366e99" + style:--non-overlay-icon-color="--fgcolor-neutral-weak"> +
+ +
+ + + {}}> + + {#each spreadsheetColumns as column, index (index)} + {@const isColumnInteractable = isCustomColumn(column.id)} + {#if column.isAction} + + + + + + {:else} + {@const columnObj = getColumn(column.id)} + {@const columnIcon = basicColumnOptions.find( + (col) => col.type === columnObj?.type + )?.icon} + {@const columnIconColor = !columnObj?.type + ? '--non-overlay-icon-color' + : '--overlay-icon-color'} + + + {#snippet children(toggle)} + { + // tablet viewport check because context-menu + // can be triggered on long hold clicks as well! + if (isColumnInteractable && !$isTabletViewport) { + toggle(event); + } + }}> + + {column.title} + + + { + if ( + isColumnInteractable && + !$isTabletViewport + ) { + toggle(event); + } + }}> + + + +
+ + + {#each basicColumnOptions as option} + { + toggle(); + updateColumn(column.id, { + type: option.type, + format: + option.format || null + }); + }}> + + + {option.name} + + + {/each} + + +
+
+
+ + + {#if !$isTabletViewport} +
+ + + {#if columnIcon} + + {/if} + + +
+ {/if} +
+
+ {/snippet} + + {#snippet tooltipChildren()} + {#if columnObj} + {@const selectedOption = getColumnOption( + columnObj.type, + columnObj.format + )} + {@const ColumnComponent = selectedOption?.component} + + + + + { + 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} + + {/if} + {/snippet} +
+ {/if} + {/each} +
+
+
+ +
+
+ + {#if $tableColumnSuggestions.thinking} +
+ + + + + + Thinking of column suggestions + + + + + resetSuggestionsStore()} + >Cancel + + + +
+ {:else if customColumns.length > 0 && showFloatingBar} +
+ + + + {#if creatingColumns} + + {/if} + + + {creatingColumns + ? 'Creating columns' + : $isSmallViewport + ? 'Review and edit suggested columns' + : 'Review and edit suggested columns before applying'} + + + + + + + (confirmDismiss = true)} + style="opacity: {creatingColumns ? '0' : '1'}" + >Dismiss + + Apply + + + + +
+ {/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? + + + diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/icon/ai.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/icon/ai.svelte new file mode 100644 index 0000000000..27bc311989 --- /dev/null +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/icon/ai.svelte @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + 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 0000000000..6a0f6f7d80 --- /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)/input.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/input.svelte new file mode 100644 index 0000000000..b663588e9a --- /dev/null +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/input.svelte @@ -0,0 +1,56 @@ + + + + + + + + + + Smart column suggestions + +
+ +
+
+ + + Enable AI to suggest useful columns based on your table name + +
+
+ + {#if $tableColumnSuggestions.enabled} +
+ +
+ {/if} +
+
+ + 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 new file mode 100644 index 0000000000..9f6ca1691a --- /dev/null +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/options.svelte @@ -0,0 +1,61 @@ + + + + {onShowStateChanged?.(showing || showSheet)} + + {#if toggleOnTapClick && $isSmallViewport} + + {:else} + + {/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]/(suggestions)/store.ts b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/store.ts new file mode 100644 index 0000000000..e0432b6f2a --- /dev/null +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/store.ts @@ -0,0 +1,136 @@ +import { writable } from 'svelte/store'; +import { columnOptions } from '../table-[table]/columns/store'; + +export type TableColumnSuggestions = { + enabled: boolean; + thinking: boolean; + 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, + table: null +}); + +export const createTableRequest = writable(false); + +export const mockSuggestions: { total: number; columns: ColumnInput[] } = { + total: 7, + columns: [ + { + name: 'title', + type: 'string', + size: 255, + format: null, + required: true, + formatOptions: null + }, + { + name: 'authorName', + type: 'string', + size: 128, + format: null, + required: true, + formatOptions: null + }, + { + name: 'publishedYear', + type: 'integer', + size: null, + format: null, + required: true, + formatOptions: { + min: 1500, + max: null + } + }, + { + name: 'genre', + type: 'string', + size: 64, + format: null, + required: false, + formatOptions: null, + default: null + }, + { + name: 'isbn', + type: 'string', + size: 13, + required: false, + formatOptions: null, + default: null + }, + { + name: 'language', + type: 'string', + size: 32, + format: null, + required: false, + formatOptions: null, + default: null + }, + { + name: 'pageCount', + type: 'integer', + required: false, + min: 1, + max: 10000, + default: 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 + })); +} + +export const basicColumnOptions = columnOptions.slice(0, -1); 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 c4b66e957f..dc39232b0d 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 4e42ee4425..37906248f3 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,25 +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, 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 @@ -35,23 +38,37 @@ name }); - showCreate = false; - subNavigation.update(); + tableColumnSuggestions.update((store) => ({ + ...store, + table: { + id: table.$id, + name: table.name + } + })); + + updateAndCleanup(); + + await onTableCreated(table); - dispatch('created', table); - addNotification({ - type: 'success', - message: `${name} has been created` - }); name = id = null; - trackEvent(Submit.TableCreate, { - customId: !!id - }); + 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 }); + } function toIdFormat(str: string): string { return str @@ -78,7 +95,7 @@ }); - + + + - - + + 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 1023088ce8..e66924433d 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 @@ -1,9 +1,11 @@ -
+
diff --git a/src/routes/(console)/project-[region]-[project]/storage/+page.svelte b/src/routes/(console)/project-[region]-[project]/storage/+page.svelte index d53830b6fa..b9c29bc764 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 2d8524fc48..198daff577 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 } },