Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"@appwrite.io/pink-icons": "0.25.0",
"@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@2cf27e0",
"@appwrite.io/pink-legacy": "^1.0.3",
"@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@2cf27e0",
"@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@40fb564",
"@faker-js/faker": "^9.9.0",
"@popperjs/core": "^2.11.8",
"@sentry/sveltekit": "^8.38.0",
Expand Down Expand Up @@ -95,5 +95,5 @@
"svelte-preprocess"
]
},
"packageManager": "[email protected].0"
"packageManager": "[email protected].1"
}
10 changes: 5 additions & 5 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion src/lib/components/confirm.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
export let disabled: boolean = false;
export let submissionLoader = false;
export let confirmDeletion: boolean = false;
export let confirmDeletionLabel: string = 'I understand and confirm';
export let onSubmit: (e: SubmitEvent) => Promise<void> | void = function () {
return;
};
Expand Down Expand Up @@ -46,7 +47,7 @@
required
id={checkboxId}
bind:checked={confirm}
label="I understand and confirm" />
label={confirmDeletionLabel} />
{/if}
</Layout.Stack>
</Layout.Stack>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -360,9 +360,7 @@
<ExpandableTable.Cell
{root}
column={col.id}
expandable={row.expandable ?? false}
isOpen={root.isOpen(row.id)}
toggle={() => root.toggle(row.id)}>
expandable={row.expandable ?? false}>
{#if col.id === 'item'}
<div class="cell-item-text">
<Typography.Text>
Expand Down Expand Up @@ -453,12 +451,7 @@
{/each}
{#if availableCredit > 0}
<ExpandableTable.Row {root} id="total-row" expandable={false}>
<ExpandableTable.Cell
{root}
column="item"
expandable={false}
isOpen={false}
toggle={() => {}}>
<ExpandableTable.Cell {root} column="item" expandable={false}>
<Layout.Stack
inline
direction="row"
Expand All @@ -471,21 +464,11 @@
>Credits</Typography.Text>
</Layout.Stack>
</ExpandableTable.Cell>
<ExpandableTable.Cell
{root}
column="usage"
expandable={false}
isOpen={false}
toggle={() => {}}>
<ExpandableTable.Cell {root} column="usage" expandable={false}>
<Typography.Text variant="m-500" color="--fgcolor-neutral-primary">
</Typography.Text>
</ExpandableTable.Cell>
<ExpandableTable.Cell
{root}
column="price"
expandable={false}
isOpen={false}
toggle={() => {}}>
<ExpandableTable.Cell {root} column="price" expandable={false}>
<Typography.Text variant="m-500" color="--fgcolor-neutral-primary">
-{formatCurrency(creditsApplied)}
</Typography.Text>
Expand All @@ -494,31 +477,16 @@
{/if}

<ExpandableTable.Row {root} id="total-row" expandable={false}>
<ExpandableTable.Cell
{root}
column="item"
expandable={false}
isOpen={false}
toggle={() => {}}>
<ExpandableTable.Cell {root} column="item" expandable={false}>
<Typography.Text variant="m-500" color="--fgcolor-neutral-primary">
Total
</Typography.Text>
</ExpandableTable.Cell>
<ExpandableTable.Cell
{root}
column="usage"
expandable={false}
isOpen={false}
toggle={() => {}}>
<ExpandableTable.Cell {root} column="usage" expandable={false}>
<Typography.Text variant="m-500" color="--fgcolor-neutral-primary">
</Typography.Text>
</ExpandableTable.Cell>
<ExpandableTable.Cell
{root}
column="price"
expandable={false}
isOpen={false}
toggle={() => {}}>
<ExpandableTable.Cell {root} column="price" expandable={false}>
<Typography.Text variant="m-500" color="--fgcolor-neutral-primary">
{formatCurrency(totalAmount)}
</Typography.Text>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,25 @@
}));
}

function createFilterableColumns(columns: Column[], selected: string[] = []): Column[] {
const idColumn = [{ id: '$id', title: '$id', type: 'string' as ColumnType }].filter(
(col) => !selected.includes(col.id)
);

const systemColumns = [
{ id: '$createdAt', title: '$createdAt', type: 'datetime' as ColumnType },
{ id: '$updatedAt', title: '$updatedAt', type: 'datetime' as ColumnType }
].filter((col) => !!selected.includes(col.id));

return [...idColumn, ...columns.filter((column) => !column.isAction), ...systemColumns];
}

Comment on lines +54 to +66
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Action columns are no longer filtered out (isAction lost in mapping).

createFilterableColumns relies on column.isAction, but createTableColumns doesn’t carry this flag over, so action columns will leak into Filters.

Apply one of the following:

  • Preserve isAction in the mapping (preferred; update the type if needed):
function createTableColumns(columns: Columns[], selected: string[] = []): Column[] {
    return columns.map((column) => ({
        id: column.key,
        title: column.key,
        type: column.type as ColumnType,
        hide: !!selected?.includes(column.key),
        array: column?.array,
        format: 'format' in column && column?.format === 'enum' ? column.format : null,
        elements: 'elements' in column ? column.elements : null,
        // ensure this is in Column type: isAction?: boolean
        isAction: (column as any)?.isAction === true
    }));
}
  • Or filter before mapping:
const raw = $table.columns.filter((c) => !c.isAction);
const freshColumns = createTableColumns(raw, selected);
🤖 Prompt for AI Agents
In
src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte
around lines 54-66, createFilterableColumns uses column.isAction but
createTableColumns drops that flag during mapping; preserve isAction when
mapping (and update the Column type to include isAction?: boolean) so action
columns remain marked and will be filtered out, or alternatively filter out
action columns from the source before mapping (e.g., $table.columns.filter(c =>
!c.isAction) then call createTableColumns) — choose the preserve-isAction
approach as preferred and ensure hide/other properties remain unchanged.

$: selected = preferences.getCustomTableColumns(page.params.table);

$: if ($table.columns) {
const freshColumns = createTableColumns($table.columns, selected);
tableColumns.set(freshColumns);
filterColumns.set(freshColumns.filter((column) => !column.isAction));
filterColumns.set(createFilterableColumns(freshColumns, selected));
}

$: hasColumns = !!$table.columns.length;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
key: '$createdAt',
type: 'datetime',
required: true,
name: 'createdAt',
name: '$createdAt',
selectable: false,
system: true
} as Models.ColumnDatetime & {
Expand All @@ -83,7 +83,7 @@
key: '$updatedAt',
type: 'datetime',
required: true,
name: 'updatedAt',
name: '$updatedAt',
selectable: false,
system: true
} as Models.ColumnDatetime & {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<script lang="ts">
import { invalidate } from '$app/navigation';
import { page } from '$app/state';
import { InputChoice } from '$lib/elements/forms';
import { addNotification } from '$lib/stores/notifications';
import { table } from '../store';
import type { Columns } from '../store';
Expand All @@ -21,7 +20,6 @@
selectedColumn: Columns | string[];
} = $props();

let checked = $state(false);
let error = $state<string | null>(null);

const selectedColumns = $derived(
Expand All @@ -38,8 +36,6 @@
.some((col) => isRelationship(col) && col.twoWay)
);

const isDeleteBtnDisabled = $derived(requiresTwoWayConfirm && !checked);

async function handleDelete() {
try {
const client = sdk.forProject(page.params.region, page.params.project);
Expand Down Expand Up @@ -75,6 +71,16 @@
function getAsRelationship(column: string | Columns): Models.ColumnRelationship {
return column as Models.ColumnRelationship;
}

const relatedColumn = $derived(
requiresTwoWayConfirm ? getAsRelationship(selectedColumns[0]) : undefined
);

const confirmDeletionLabel = $derived(
!requiresTwoWayConfirm
? 'I understand and confirm'
: `Delete relationship between ${relatedColumn.key} to ${relatedColumn.twoWayKey}`
);
Comment on lines +75 to +83
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix two‑way label selection to avoid wrong column and runtime crash

requiresTwoWayConfirm can be true even when selectedColumns[0] isn’t the two‑way relationship (or is a string). Accessing relatedColumn.key in that case will be incorrect and can throw. Find the actual two‑way relationship column instead, and fix the label grammar (“between … and …”).

Apply this diff:

-    const relatedColumn = $derived(
-        requiresTwoWayConfirm ? getAsRelationship(selectedColumns[0]) : undefined
-    );
-
-    const confirmDeletionLabel = $derived(
-        !requiresTwoWayConfirm
-            ? 'I understand and confirm'
-            : `Delete relationship between ${relatedColumn.key} to ${relatedColumn.twoWayKey}`
-    );
+    const twoWayColumn = $derived(
+        selectedColumns.find(
+            (c): c is Models.ColumnRelationship =>
+                typeof c !== 'string' && isRelationship(c) && c.twoWay
+        )
+    );
+
+    const confirmDeletionLabel = $derived(
+        twoWayColumn
+            ? `Delete relationship between ${twoWayColumn.key} and ${twoWayColumn.twoWayKey}`
+            : 'I understand and confirm'
+    );

Note: If you adopt this, getAsRelationship becomes unused and can be removed.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const relatedColumn = $derived(
requiresTwoWayConfirm ? getAsRelationship(selectedColumns[0]) : undefined
);
const confirmDeletionLabel = $derived(
!requiresTwoWayConfirm
? 'I understand and confirm'
: `Delete relationship between ${relatedColumn.key} to ${relatedColumn.twoWayKey}`
);
const twoWayColumn = $derived(
selectedColumns.find(
(c): c is Models.ColumnRelationship =>
typeof c !== 'string' && isRelationship(c) && c.twoWay
)
);
const confirmDeletionLabel = $derived(
twoWayColumn
? `Delete relationship between ${twoWayColumn.key} and ${twoWayColumn.twoWayKey}`
: 'I understand and confirm'
);

</script>

<Confirm
Expand All @@ -83,7 +89,7 @@
title="Delete column"
bind:error
confirmDeletion
disabled={isDeleteBtnDisabled}>
{confirmDeletionLabel}>
{#if selectedColumns.length === 1}
<p>
Are you sure you want to delete <b data-private>{selectedKeys[0]}</b> from
Expand All @@ -98,19 +104,12 @@

{#if requiresTwoWayConfirm}
<!-- not allowed on multi selections, safe to assume that this isn't a string! -->
{@const column = getAsRelationship(selectedColumns[0])}
<Layout.Stack direction="column" gap="xl">
<p>
This is a two way relationship and the corresponding relationship will also be
deleted.
</p>
<p><b>This action is irreversible.</b></p>
<ul>
<InputChoice id="delete" label="Delete" showLabel={false} bind:value={checked}>
Delete relationship between <b data-private>{column.key}</b> to
<b data-private>{column.twoWayKey}</b>
</InputChoice>
</ul>
</Layout.Stack>
{/if}
</Confirm>
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,7 @@
function generateOptions(
loading: boolean,
rows: Models.Row[] | undefined,
column: Models.ColumnRelationship,
editing: boolean
column: Models.ColumnRelationship
): SelectOption[] {
if (loading) {
return [{ label: 'Loading...', value: null, disabled: true }];
Expand All @@ -139,15 +138,11 @@
.map((name) => row?.[name])
.filter((value) => value != null && typeof value === 'string' && value !== '');

const displayValues = !editing
? values
: values.map((value) => (value.length > 5 ? value.slice(0, 5) + '...' : value));

let label: string;
if (!values.length) {
label = row.$id;
} else {
label = `${row.$id} (${displayValues.join(' | ')})`;
label = `${values.join(' | ')} (...${row.$id.slice(-5)})`;
}

return {
Expand Down Expand Up @@ -183,7 +178,7 @@

$: totalCount = relatedList?.length ?? 0;

$: options = generateOptions(loadingRelationships, rowList?.rows, column, editing);
$: options = generateOptions(loadingRelationships, rowList?.rows, column);

$: hasItems = totalCount > 0;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@
</div>
{:else if $table.columns?.length && work}
<div bind:this={columnFormWrapper}>
<Layout.Stack direction="column" gap="l">
<Layout.Stack direction="column" gap="xl">
{#each $table.columns as column}
{@const label = column.key}
<ColumnItem
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,11 @@
<ActionMenu.Root width="180px">
{#each menuItems as item, index (index)}
{#if item.divider}
<div style:padding-block="0.5rem" style:margin-inline="-1rem;">
{@const isLastDivider = index === menuItems.length - 2}
<div
style:margin-inline="-1rem"
style:padding-block-start="0.5rem"
style:padding-block-end={isLastDivider ? '0.25rem' : '0.5rem'}>
<Divider />
</div>
{:else if shouldShow(item)}
Expand All @@ -181,7 +185,11 @@
</Popover>

<style>
.action-menu-root :global(:first-child) {
overflow: visible;
.action-menu-root {
margin-inline-start: calc(var(--space-2) * -1);

& :global(:first-child) {
overflow: visible;
}
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -106,15 +106,6 @@

const minimumWidth = 168;
const emptyCellsLimit = $isSmallViewport ? 12 : 18;
const SYSTEM_KEYS = new Set([
'$tableId',
'$databaseId',
'$permissions',
'$createdAt',
'$updatedAt',
'$id',
'$sequence'
]); /* TODO: should be fixed at the sdk level! */

let selectedRows = [];
let spreadsheetContainer: SpreadsheetContainer;
Expand Down Expand Up @@ -576,16 +567,11 @@

async function updateRowContents(row: Models.Row) {
try {
const onlyData = Object.fromEntries(
Object.entries(row).filter(([key]) => !SYSTEM_KEYS.has(key))
);

// TODO | BUG: related rows still have `system` columns atm!
await sdk.forProject(page.params.region, page.params.project).tablesDB.updateRow({
databaseId,
tableId: $table.$id,
rowId: row.$id,
data: onlyData,
data: row,
permissions: row.$permissions
});

Expand Down