mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
feat: column suggestions modal.
This commit is contained in:
+2
-2
@@ -24,9 +24,9 @@
|
||||
"@ai-sdk/svelte": "^1.1.24",
|
||||
"@appwrite.io/console": "https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@636ed39",
|
||||
"@appwrite.io/pink-icons": "0.25.0",
|
||||
"@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@754a996",
|
||||
"@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@6faeea0",
|
||||
"@appwrite.io/pink-legacy": "^1.0.3",
|
||||
"@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@754a996",
|
||||
"@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@6faeea0",
|
||||
"@faker-js/faker": "^9.9.0",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@sentry/sveltekit": "^8.38.0",
|
||||
|
||||
Generated
+10
-10
@@ -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@754a996
|
||||
version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@754a996(svelte@5.25.3)
|
||||
specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@6faeea0
|
||||
version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@6faeea0(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@754a996
|
||||
version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@754a996(svelte@5.25.3)
|
||||
specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@6faeea0
|
||||
version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@6faeea0(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@754a996':
|
||||
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@754a996}
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@6faeea0':
|
||||
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@6faeea0}
|
||||
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@754a996':
|
||||
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@754a996}
|
||||
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@6faeea0':
|
||||
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@6faeea0}
|
||||
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@754a996(svelte@5.25.3)':
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@6faeea0(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@754a996(svelte@5.25.3)':
|
||||
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@6faeea0(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
|
||||
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/state';
|
||||
import { resolve } from '$app/paths';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
import Input from './input.svelte';
|
||||
import { Modal } from '$lib/components';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { tableColumnSuggestions } from './store';
|
||||
|
||||
let {
|
||||
show = $bindable(false)
|
||||
}: {
|
||||
show?: boolean;
|
||||
} = $props();
|
||||
|
||||
const isOnRowsPage = $derived(page.route?.id?.endsWith('table-[table]'));
|
||||
|
||||
function resetSuggestionsStore() {
|
||||
show = false;
|
||||
|
||||
$tableColumnSuggestions.table = null;
|
||||
$tableColumnSuggestions.context = null;
|
||||
|
||||
$tableColumnSuggestions.enabled = false;
|
||||
$tableColumnSuggestions.thinking = false;
|
||||
$tableColumnSuggestions.force = undefined;
|
||||
}
|
||||
|
||||
async function triggerColumnSuggestions() {
|
||||
if (!isOnRowsPage) {
|
||||
await goto(
|
||||
resolve(
|
||||
'/(console)/project-[region]-[project]/databases/database-[database]/table-[table]',
|
||||
{
|
||||
region: page.params.region,
|
||||
project: page.params.project,
|
||||
database: page.params.database,
|
||||
table: page.params.table
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
show = false;
|
||||
$tableColumnSuggestions.enabled = true;
|
||||
$tableColumnSuggestions.table = {
|
||||
id: page.params.table,
|
||||
name: page.data.table?.name ?? 'Table'
|
||||
};
|
||||
$tableColumnSuggestions.force = true;
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal bind:show title="Suggest columns" onSubmit={triggerColumnSuggestions}>
|
||||
<Input isModal />
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<Button text on:click={resetSuggestionsStore}>Cancel</Button>
|
||||
<Button submit>Generate columns</Button>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
+13
-1
@@ -43,6 +43,12 @@
|
||||
|
||||
import IconAINotification from './icon/aiNotification.svelte';
|
||||
|
||||
let {
|
||||
userColumns = []
|
||||
}: {
|
||||
userColumns?: Column[];
|
||||
} = $props();
|
||||
|
||||
let resizeObserver: ResizeObserver;
|
||||
let spreadsheetContainer: HTMLElement;
|
||||
|
||||
@@ -394,6 +400,7 @@
|
||||
...baseColProps
|
||||
},
|
||||
...finalCustomColumns,
|
||||
/*...userColumns,*/
|
||||
{
|
||||
id: 'actions',
|
||||
title: '',
|
||||
@@ -409,6 +416,8 @@
|
||||
const emptyCells = $derived(($isSmallViewport ? 14 : 17) + (!$expandTabs ? 2 : 0));
|
||||
|
||||
onMount(async () => {
|
||||
userColumns; /* silences lint check, variable not read */
|
||||
|
||||
if (spreadsheetContainer) {
|
||||
resizeObserver = new ResizeObserver(recalcAll);
|
||||
resizeObserver.observe(spreadsheetContainer);
|
||||
@@ -430,6 +439,7 @@
|
||||
$tableColumnSuggestions.enabled = false;
|
||||
}
|
||||
|
||||
$tableColumnSuggestions.force = undefined;
|
||||
$tableColumnSuggestions.context = null;
|
||||
$tableColumnSuggestions.thinking = false;
|
||||
|
||||
@@ -739,7 +749,7 @@
|
||||
tick().then(() => {
|
||||
selectedColumnId = columnData.key;
|
||||
selectedColumnName = columnData.key;
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -888,6 +898,8 @@
|
||||
timeout: NOTIFICATION_AND_MOCK_DELAY
|
||||
});
|
||||
|
||||
resetSuggestionsStore(true);
|
||||
|
||||
// show index notification!
|
||||
showIndexSuggestionsNotification();
|
||||
|
||||
|
||||
+10
-2
@@ -7,6 +7,12 @@
|
||||
import { Button, InputTextarea } from '$lib/elements/forms';
|
||||
import { Card, Layout, Selector, Typography } from '@appwrite.io/pink-svelte';
|
||||
|
||||
const {
|
||||
isModal = false
|
||||
}: {
|
||||
isModal?: boolean;
|
||||
} = $props();
|
||||
|
||||
onMount(() => {
|
||||
if (featureActive) {
|
||||
$tableColumnSuggestions.enabled = true;
|
||||
@@ -23,7 +29,9 @@
|
||||
|
||||
const subtitle = $derived.by(() => {
|
||||
return featureActive
|
||||
? 'Enable AI to suggest useful columns based on your table name'
|
||||
? isModal
|
||||
? 'Use AI to suggest useful columns'
|
||||
: 'Enable AI to suggest useful columns based on your table name'
|
||||
: 'Sign up for Cloud to generate columns based on your table name';
|
||||
});
|
||||
</script>
|
||||
@@ -42,7 +50,7 @@
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
|
||||
{#if featureActive}
|
||||
{#if featureActive && !isModal}
|
||||
<div class="suggestions-switch">
|
||||
<Selector.Switch
|
||||
id="suggestions"
|
||||
|
||||
+3
@@ -3,6 +3,7 @@ import { IndexType } from '@appwrite.io/console';
|
||||
import { columnOptions } from '../table-[table]/columns/store';
|
||||
|
||||
export type TableColumnSuggestions = {
|
||||
force?: boolean;
|
||||
enabled: boolean;
|
||||
thinking: boolean;
|
||||
context?: string | undefined;
|
||||
@@ -48,6 +49,8 @@ export const tableColumnSuggestions = writable<TableColumnSuggestions>({
|
||||
|
||||
export const showIndexesSuggestions = writable<boolean>(false);
|
||||
|
||||
export const showColumnsSuggestionsModal = writable<boolean>(false);
|
||||
|
||||
export const mockSuggestions: { total: number; columns: ColumnInput[] } = {
|
||||
total: 7,
|
||||
columns: [
|
||||
|
||||
+8
-1
@@ -65,7 +65,12 @@
|
||||
import { Submit, trackEvent } from '$lib/actions/analytics';
|
||||
|
||||
import IndexesSuggestions from '../(suggestions)/indexes.svelte';
|
||||
import { showIndexesSuggestions, tableColumnSuggestions } from '../(suggestions)';
|
||||
import ColumnsSuggestions from '../(suggestions)/columns.svelte';
|
||||
import {
|
||||
showColumnsSuggestionsModal,
|
||||
showIndexesSuggestions,
|
||||
tableColumnSuggestions
|
||||
} from '../(suggestions)';
|
||||
|
||||
let editRow: EditRow;
|
||||
let editRelatedRow: EditRelatedRow;
|
||||
@@ -482,4 +487,6 @@
|
||||
</svelte:fragment>
|
||||
</Dialog>
|
||||
|
||||
<ColumnsSuggestions bind:show={$showColumnsSuggestionsModal} />
|
||||
|
||||
<IndexesSuggestions />
|
||||
|
||||
+8
-4
@@ -38,7 +38,11 @@
|
||||
import CreateRow from './rows/create.svelte';
|
||||
import { onDestroy } from 'svelte';
|
||||
import { isCloud } from '$lib/system';
|
||||
import { Empty as SuggestionsEmptySheet, tableColumnSuggestions } from '../(suggestions)';
|
||||
import {
|
||||
Empty as SuggestionsEmptySheet,
|
||||
tableColumnSuggestions,
|
||||
showColumnsSuggestionsModal
|
||||
} from '../(suggestions)';
|
||||
import EmptySheetCards from './layout/emptySheetCards.svelte';
|
||||
import IconAI from '../(suggestions)/icon/aiForButton.svelte';
|
||||
|
||||
@@ -201,7 +205,7 @@
|
||||
</Container>
|
||||
|
||||
<div class="databases-spreadsheet">
|
||||
{#if hasColumns && hasValidColumns}
|
||||
{#if hasColumns && hasValidColumns && $tableColumnSuggestions.force === false}
|
||||
{#if data.rows.total}
|
||||
<Divider />
|
||||
<SpreadSheet {data} bind:showRowCreateSheet={$showRowCreateSheet} />
|
||||
@@ -250,7 +254,7 @@
|
||||
</EmptySheet>
|
||||
{/if}
|
||||
{:else if isCloud && canShowSuggestionsSheet}
|
||||
<SuggestionsEmptySheet />
|
||||
<SuggestionsEmptySheet userColumns={$tableColumns} />
|
||||
{:else}
|
||||
<EmptySheet mode="rows" showActions={$canWriteTables} title="You have no columns yet">
|
||||
{#snippet actions()}
|
||||
@@ -267,7 +271,7 @@
|
||||
title="Suggest columns"
|
||||
subtitle="Use AI to generate columns"
|
||||
onClick={() => {
|
||||
// todo: add a modal and show input, no toggle.
|
||||
$showColumnsSuggestionsModal = true;
|
||||
}} />
|
||||
|
||||
<EmptySheetCards
|
||||
|
||||
+37
-56
@@ -1,9 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/state';
|
||||
import { type Columns, type ColumnDirection } from './store';
|
||||
import { goto, invalidate } from '$app/navigation';
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Layout } from '@appwrite.io/pink-svelte';
|
||||
import { Alert, Layout, Link } from '@appwrite.io/pink-svelte';
|
||||
import { InputSelect, InputText } from '$lib/elements/forms';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
@@ -11,8 +10,10 @@
|
||||
import type { Column } from '$lib/helpers/types';
|
||||
import { preferences } from '$lib/stores/preferences';
|
||||
import { onMount } from 'svelte';
|
||||
import { resolve } from '$app/paths';
|
||||
import { Input as SuggestionsInput, tableColumnSuggestions } from '../(suggestions)/index';
|
||||
|
||||
import { showColumnsSuggestionsModal } from '../(suggestions)/store';
|
||||
import IconAINotification from '../(suggestions)/icon/aiNotification.svelte';
|
||||
import { type Columns, type ColumnDirection, showCreateColumnSheet } from './store';
|
||||
|
||||
let {
|
||||
direction = null,
|
||||
@@ -52,16 +53,6 @@
|
||||
columnOptions.find((option) => option.name === selectedOption).component
|
||||
);
|
||||
|
||||
function initSuggestionsStore() {
|
||||
if (!isSuggestionsFeatureEnabled) return;
|
||||
|
||||
$tableColumnSuggestions.table = null;
|
||||
$tableColumnSuggestions.enabled = false;
|
||||
|
||||
$tableColumnSuggestions.context = null;
|
||||
$tableColumnSuggestions.thinking = false;
|
||||
}
|
||||
|
||||
function init() {
|
||||
key = null;
|
||||
$option = null;
|
||||
@@ -74,9 +65,6 @@
|
||||
/* default to string */
|
||||
selectedOption = 'String';
|
||||
$option = columnOptions[0];
|
||||
|
||||
/* init suggestions */
|
||||
initSuggestionsStore();
|
||||
}
|
||||
|
||||
function insertColumnInOrder() {
|
||||
@@ -155,33 +143,6 @@
|
||||
}
|
||||
|
||||
export async function submit() {
|
||||
if (isSuggestionsEnabled) {
|
||||
const tableInUse = page.data.table;
|
||||
// TODO: confirm what flow to use!
|
||||
tableColumnSuggestions.update((store) => ({
|
||||
...store,
|
||||
thinking: true,
|
||||
table: {
|
||||
id: tableInUse.$id,
|
||||
name: tableInUse.name
|
||||
}
|
||||
}));
|
||||
|
||||
const { region, project, database, table } = page.params;
|
||||
await goto(
|
||||
resolve(
|
||||
'/(console)/project-[region]-[project]/databases/database-[database]/table-[table]',
|
||||
{
|
||||
region,
|
||||
project,
|
||||
database,
|
||||
table
|
||||
}
|
||||
)
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
await $option.create(databaseId, tableId, key, data);
|
||||
|
||||
@@ -222,15 +183,23 @@
|
||||
$option = columnOptions.find((option) => option.name === selectedOption);
|
||||
}
|
||||
});
|
||||
|
||||
const isSuggestionsEnabled = $derived(
|
||||
isSuggestionsFeatureEnabled && $tableColumnSuggestions.enabled
|
||||
);
|
||||
</script>
|
||||
|
||||
<Layout.Stack gap="xl">
|
||||
{#if isSuggestionsFeatureEnabled}
|
||||
<SuggestionsInput />
|
||||
<div class="custom-inline-alert">
|
||||
<Alert.Inline>
|
||||
<svelte:fragment slot="icon">
|
||||
<IconAINotification />
|
||||
</svelte:fragment>
|
||||
|
||||
Need help? Let AI <Link.Button
|
||||
on:click={() => {
|
||||
$showCreateColumnSheet.show = false;
|
||||
$showColumnsSuggestionsModal = true;
|
||||
}}>suggest columns</Link.Button> based on your data
|
||||
</Alert.Inline>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<Layout.Stack direction="row">
|
||||
@@ -240,7 +209,7 @@
|
||||
placeholder="Enter key"
|
||||
bind:value={key}
|
||||
autofocus
|
||||
disabled={selectedOption === 'Relationship' || isSuggestionsEnabled}
|
||||
disabled={selectedOption === 'Relationship'}
|
||||
required
|
||||
pattern="^[A-Za-z0-9][A-Za-z0-9._\-]*$" />
|
||||
|
||||
@@ -248,7 +217,6 @@
|
||||
id="type"
|
||||
label="Type"
|
||||
bind:value={selectedOption}
|
||||
disabled={isSuggestionsEnabled}
|
||||
options={columnOptions.map((attr) => {
|
||||
return {
|
||||
label: attr.name,
|
||||
@@ -260,9 +228,22 @@
|
||||
</Layout.Stack>
|
||||
|
||||
{#if selectedOption}
|
||||
<ColumnComponent
|
||||
disabled={isSuggestionsEnabled}
|
||||
bind:data
|
||||
onclose={() => ($option = null)} />
|
||||
<ColumnComponent bind:data onclose={() => ($option = null)} />
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
|
||||
<style lang="scss">
|
||||
.custom-inline-alert {
|
||||
& :global(article) {
|
||||
padding: var(--space-4, 8px);
|
||||
}
|
||||
|
||||
& :global(div:first-child > :nth-child(2)) {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
& :global(.ai-icon-holder.notification) {
|
||||
height: 36px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
+3
-3
@@ -37,9 +37,9 @@
|
||||
import { showCreateColumnSheet } from '../store';
|
||||
import { isSmallViewport } from '$lib/stores/viewport';
|
||||
import { page } from '$app/state';
|
||||
import { showIndexesSuggestions } from '../../(suggestions)';
|
||||
import { showIndexesSuggestions, showColumnsSuggestionsModal } from '../../(suggestions)';
|
||||
import IconAI from '../../(suggestions)/icon/aiForButton.svelte';
|
||||
import EmptySheetCards from '$routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/layout/emptySheetCards.svelte';
|
||||
import EmptySheetCards from '../layout/emptySheetCards.svelte';
|
||||
|
||||
let {
|
||||
data
|
||||
@@ -339,7 +339,7 @@
|
||||
title="Suggest columns"
|
||||
subtitle="Use AI to generate columns"
|
||||
onClick={() => {
|
||||
// TODO: add a modal and show input, no toggle.
|
||||
$showColumnsSuggestionsModal = true;
|
||||
}} />
|
||||
{/snippet}
|
||||
</EmptySheet>
|
||||
|
||||
Reference in New Issue
Block a user