-
Notifications
You must be signed in to change notification settings - Fork 198
CSV export #2496
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
CSV export #2496
Conversation
ConsoleProject ID: Sites (1)
Tip Cursor pagination performs better than offset pagination when loading further pages. |
WalkthroughAdds CSV export functionality: a new table-level export wizard page to configure and create CSV export migrations; a realtime, collapsible CSV export progress component that tracks migrations, auto-downloads completed exports, and surfaces errors; re-exports CsvExportBox from components index and integrates it into the project layout; toolbar UI changes in the table view to add import/export icon actions and an export URL helper; two new analytics enum variants for CSV export events; a new Estimated code review effort🎯 3 (Moderate) | ⏱️ ~30 minutes
Pre-merge checks and finishing touches❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✨ Finishing touches🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🧹 Nitpick comments (6)
src/routes/(console)/project-[region]-[project]/settings/domains/table.svelte (1)
95-105: Good fix; add Retry analytics and avoid string-literal drift.
- Condition now correctly excludes 'verifying' from Retry. OK.
- Add tracking for Retry to match Delete.
- Consider centralizing status literals (enum/const) to prevent future typos.
Apply:
{#if domain.status !== 'verified' && domain.status !== 'verifying'} <ActionMenu.Item.Button leadingIcon={IconRefresh} on:click={(e) => { selectedDomain = domain; showRetry = true; + trackEvent(Click.DomainRetryDomainVerificationClick, { + source: 'settings_domain_overview' + }); toggle(e); }}> Retry </ActionMenu.Item.Button> {/if}src/routes/(console)/project-[region]-[project]/functions/function-[function]/executions/table.svelte (1)
110-113: Avoidascast in runtime checks.Coerce safely to string to keep types narrow without assertions.
- disabled={!log?.scheduledAt || (status as string) !== 'scheduled'} + disabled={!log?.scheduledAt || String(status) !== 'scheduled'}src/routes/(console)/project-[region]-[project]/functions/function-[function]/executions/sheet.svelte (1)
113-116: Mirror: prefer safe coercion overascast.Keeps typing honest and avoids accidental non-string comparisons.
- disabled={!selectedLog?.scheduledAt || - (selectedLog.status as string) !== 'scheduled'} + disabled={!selectedLog?.scheduledAt || + String(selectedLog.status) !== 'scheduled'}src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte (1)
29-30: Track user actions and close the popover on click.
- Emit Click events for Import/Export to complement Submit metrics.
- Close the popover before opening picker or navigating.
- import { Submit, trackError, trackEvent } from '$lib/actions/analytics'; + import { Submit, Click, trackError, trackEvent } from '$lib/actions/analytics'; @@ - <ActionMenu.Item.Button - leadingIcon={IconUpload} - on:click={() => (showImportCSV = true)}> + <ActionMenu.Item.Button + leadingIcon={IconUpload} + on:click={() => { + trackEvent(Click.DatabaseImportCsv); + toggle(); + showImportCSV = true; + }}> Import CSV </ActionMenu.Item.Button> <ActionMenu.Item.Button leadingIcon={IconDownload} - on:click={() => - goto( + on:click={() => { + trackEvent(Click.DatabaseExportCsv); + toggle(); + goto( `${base}/project-${page.params.region}-${page.params.project}/databases/database-${page.params.database}/table-${page.params.table}/export` - )}> + ); + }}> Export CSV </ActionMenu.Item.Button>Also applies to: 169-193
src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/export/+page.svelte (1)
139-146: Harden error handling for unknown error shapes
error.messagemay be undefined. Normalize to a string to avoid runtime issues.-} catch (error) { - addNotification({ - type: 'error', - message: error.message - }); - - trackError(error, Submit.DatabaseExportCsv); -} +} catch (error) { + const message = error instanceof Error ? error.message : String(error); + addNotification({ type: 'error', message }); + trackError(error, Submit.DatabaseExportCsv); +}src/lib/components/csvExportBox.svelte (1)
92-95: Normalize error to string in download failure path
e.messagemay be undefined. Convert to a safe string.- } catch (e) { + } catch (e) { addNotification({ type: 'error', - message: `Failed to download file: ${e.message}` + message: `Failed to download file: ${e instanceof Error ? e.message : String(e)}` }); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (11)
package.json(2 hunks)src/lib/actions/analytics.ts(2 hunks)src/lib/components/csvExportBox.svelte(1 hunks)src/lib/components/index.ts(1 hunks)src/routes/(console)/project-[region]-[project]/+layout.svelte(2 hunks)src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte(4 hunks)src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/export/+page.svelte(1 hunks)src/routes/(console)/project-[region]-[project]/functions/function-[function]/executions/sheet.svelte(1 hunks)src/routes/(console)/project-[region]-[project]/functions/function-[function]/executions/table.svelte(1 hunks)src/routes/(console)/project-[region]-[project]/overview/platforms/+page.svelte(2 hunks)src/routes/(console)/project-[region]-[project]/settings/domains/table.svelte(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: e2e
- GitHub Check: build
🔇 Additional comments (7)
package.json (2)
98-98: Pnpm version bump is verified as safe—no breaking changes between versions.pnpm 10.18.3 is a patch release with bug fixes, not breaking changes relative to 10.15.1. The version bump from 10.15.1 to 10.18.3 is a routine minor version update with only patch-level fixes and poses no compatibility concerns.
25-25: The review comment's concerns about pkg.pr.new are overstated and based on incomplete context.The git diff shows that the
pkg.pr.newpattern for@appwrite.io/consolealready exists inorigin/main(commit636ed39). This PR updates the commit reference to14f6db1within the same preview package service—it does not introduce the pattern. Additionally,pkg.vcdependencies are already established at lines 27–29 in the codebase.The flagged concerns (reliability, reproducibility, maintenance burden) would apply to the existing main branch, not to this change. If the
pkg.pr.newapproach requires architectural or process changes, that decision should be addressed upstream, not as a blocker for this feature branch.The
pnpmversion bump (10.15.1 → 10.18.3) is a minor routine update with no critical concerns.Likely an incorrect or invalid review comment.
src/lib/components/index.ts (1)
16-16: Public export added correctly.CsvExportBox is now available via components barrel. LGTM.
src/routes/(console)/project-[region]-[project]/+layout.svelte (1)
2-2: CsvExportBox integration looks good.Assuming it self-hides when inactive, this matches existing pattern. No issues.
Also applies to: 124-125
src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte (2)
186-189: Export route verified as present and correctly configured.The export route exists at the correct path:
src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/export/+page.svelte. The navigation URL in the code correctly maps to this route structure.
98-106: <!-- [scratchpad_start]
[task verification_2/2 final]
[observations]
- Migrations class imported from @appwrite.io/console (line 19 of sdk.ts)
- Instantiated at lines 86 and 131 in sdk.ts
- Method called:
migrations.createCSVImport({bucketId, fileId, resourceId, internalFile})- Package.json uses PR build:
https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@14f6db1- npm view shows published version is 1.10.0
- Web search found NO public documentation for
migrations.createCSVImport- Method not found in released SDK version
- Only exists in PR build being used
[analysis]- Critical risk: method is NOT in the public/released SDK
- Only available in experimental PR build
- Method signature and availability cannot be confirmed against stable release
- This is exactly what the review comment asked to verify
[pending]- No way to verify if PR build has this method without access to PR source
- Codebase will break if PR build is not merged or if method signature changes
[done]- Confirmed review concern is valid and actionable
- Method requires verification against actual SDK being shipped
[scratchpad_start] -->Verify that
migrations.createCSVImportmethod exists and is stable in the PR build of @appwrite.io/console currently being shipped.The method is called at lines 98–106 but does not exist in the published @appwrite.io/console@1.10.0. It only exists in the PR build (
@appwrite.io/console@https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@14f6db1). Confirm:
- The PR build has been tested and includes this method.
- The method signature matches:
createCSVImport({ bucketId, fileId, resourceId, internalFile }).- The method will remain stable after the PR is merged.
src/lib/components/csvExportBox.svelte (1)
255-264: Svelte event binding bug: useon:click, notonclick
onclick={...}won’t execute as Svelte expects; handlers won’t fire. Switch toon:click.-<button +<button class="upload-box-button" class:is-open={isOpen} aria-label="toggle upload box" - onclick={() => (isOpen = !isOpen)}> + on:click={() => (isOpen = !isOpen)}>-<button class="upload-box-button" aria-label="close export box" onclick={clear}> +<button class="upload-box-button" aria-label="close export box" on:click={clear}>⛔ Skipped due to learnings
Learnt from: ItzNotABug PR: appwrite/console#2373 File: src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/options.svelte:33-42 Timestamp: 2025-09-25T04:31:05.213Z Learning: Svelte 5 uses `onclick` instead of `on:click` for event handlers. Event handlers are now properties like any other rather than directives that require the `on:` prefix.
...)/project-[region]-[project]/databases/database-[database]/table-[table]/export/+page.svelte
Outdated
Show resolved
Hide resolved
...)/project-[region]-[project]/databases/database-[database]/table-[table]/export/+page.svelte
Show resolved
Hide resolved
src/routes/(console)/project-[region]-[project]/overview/platforms/+page.svelte
Outdated
Show resolved
Hide resolved
...)/project-[region]-[project]/databases/database-[database]/table-[table]/export/+page.svelte
Outdated
Show resolved
Hide resolved
# Conflicts: # package.json # pnpm-lock.yaml # src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
package.json(2 hunks)src/lib/components/csvExportBox.svelte(1 hunks)src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte(5 hunks)src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/export/+page.svelte(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- package.json
- src/lib/components/csvExportBox.svelte
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-09-30T07:41:06.679Z
Learnt from: ItzNotABug
Repo: appwrite/console PR: 2425
File: src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte:454-468
Timestamp: 2025-09-30T07:41:06.679Z
Learning: In `src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte`, the column suggestions API (console.suggestColumns) has a maximum limit of 7 columns returned, which aligns with the initial placeholder count of 7 in customColumns.
Applied to files:
src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.sveltesrc/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/export/+page.svelte
📚 Learning: 2025-10-13T05:13:54.542Z
Learnt from: ItzNotABug
Repo: appwrite/console PR: 2413
File: src/routes/(console)/project-[region]-[project]/databases/table.svelte:33-39
Timestamp: 2025-10-13T05:13:54.542Z
Learning: In Svelte 5, `import { page } from '$app/state'` provides a reactive state proxy that can be accessed directly (e.g., `page.params`), unlike the older `import { page } from '$app/stores'` which returns a readable store requiring the `$page` syntax for auto-subscription in components.
Applied to files:
src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte
📚 Learning: 2025-09-08T13:20:47.308Z
Learnt from: ItzNotABug
Repo: appwrite/console PR: 2316
File: src/routes/(console)/project-[region]-[project]/functions/create-function/deploy/+page.svelte:29-29
Timestamp: 2025-09-08T13:20:47.308Z
Learning: The Form.svelte component in the Appwrite console creates a FormContext with isSubmitting as writable(false) and expects consumers to work with Svelte writable stores, not plain booleans.
Applied to files:
src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/export/+page.svelte
📚 Learning: 2025-09-25T04:33:19.632Z
Learnt from: ItzNotABug
Repo: appwrite/console PR: 2373
File: src/routes/(console)/project-[region]-[project]/databases/database-[database]/createTable.svelte:28-33
Timestamp: 2025-09-25T04:33:19.632Z
Learning: In the Appwrite console createTable component, manual submit state management (like setting creatingTable = true) is not needed because: 1) The Modal component handles submit state internally via submissionLoader, preventing double submissions, and 2) After successful table creation, the entire view is destroyed and replaced with the populated table view, ending the component lifecycle.
Applied to files:
src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/export/+page.svelte
🔇 Additional comments (10)
src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte (4)
5-6: LGTM! Imports support new navigation and UI.The new imports for navigation (
goto,resolve) and icons (IconUpload,IconDownload) properly support the CSV export and improved UI functionality.Also applies to: 35-37
104-104: LGTM! Naming convention update.The change from
createCsvMigrationtocreateCSVImportfollows consistent PascalCase naming, aligning withcreateCSVExportused elsewhere in this PR.
128-140: LGTM! Clean URL construction with query preservation.The function properly uses
resolvefor type-safe path construction and preserves the current query parameter, ensuring filter state is maintained when navigating to the export page.
192-237: LGTM! Well-structured UI improvements.The new tooltip-wrapped icon buttons provide better UX with space-efficient layout. Analytics tracking for the export action is properly implemented, and the navigation flow to the export wizard is clean.
src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/export/+page.svelte (6)
29-62: LGTM! Clean Svelte 5 state management.The state variables properly use
$stateand$derivedrunes. TheisSubmittingwritable correctly binds to the Form component's internal state (line 147), and derived values likelocalTags,visibleColumns,selectedColumnCount, andtableUrlare computed efficiently using$derivedand$derived.by.
65-77: LGTM! Type-safe URL construction with query preservation.The
tableUrlderived value properly usesresolvefor type-safe path construction and preserves the query parameter to maintain filter state when navigating back to the table view.
79-94: LGTM! Clean helper functions.The helper functions are well-structured. The
removeLocalFilterfunction correctly creates a new Map (line 81) to ensure Svelte's reactivity is triggered.
96-138: LGTM! Robust export handler with validation and error handling.The export function includes proper validation (at least one column selected), comprehensive error handling with user notifications, and analytics tracking for both success and error cases. The API call correctly maps export options and conditionally includes filters based on the
exportWithFilterscheckbox.
146-251: LGTM! Well-structured wizard UI with responsive layout.The wizard form is well-organized with clear sections for column selection and export options. The responsive grid layout (
columns={3} columnsS={1}) adapts properly to different viewports. The "Export with filters" checkbox is appropriately disabled when no filters exist, and the Export button correctly validates that at least one column is selected before allowing submission.
140-144: LGTM! Proper initialization and UX styling.The
onMounthook correctly initializes column selections and creates a local copy of queries from the store. The CSS class for disabled checkboxes improves UX by removing the pointer cursor when the "Export with filters" option is unavailable.Also applies to: 253-257
...)/project-[region]-[project]/databases/database-[database]/table-[table]/export/+page.svelte
Show resolved
Hide resolved
# Conflicts: # package.json # pnpm-lock.yaml # src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte
# Conflicts: # package.json # pnpm-lock.yaml # src/routes/(console)/project-[region]-[project]/functions/create-function/template-[template]/+page.svelte # src/routes/(console)/project-[region]-[project]/sites/create-site/templates/template-[template]/+page.svelte
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (3)
src/lib/components/csvExportBox.svelte (1)
66-86: XSS risk via HTML injection in notifications and@htmlrendering.The code renders unescaped HTML from
tableNameandbucketNameviaaddNotification({ isHtml: true, ... })and{@html text(...)}. If a user creates a table or bucket with a malicious name (e.g.,<script>alert('xss')</script>), it will execute in the browser.Apply the escaping fix suggested in the prior review, or refactor to avoid HTML strings entirely by rendering names in Svelte markup (e.g.,
<b>{tableName}</b>instead of building HTML strings).src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/export/+page.svelte (2)
143-147: Add preventDefault to utility buttons inside form.The "Select all" and "Deselect all" buttons are inside the
<Form>component and may trigger form submission when clicked. Add thepreventDefaultmodifier or settype="button"to prevent accidental submissions.Apply this diff:
-<Button compact on:click={selectAllColumns}>Select all</Button> +<Button compact on:click|preventDefault={selectAllColumns}>Select all</Button>-<Button compact on:click={deselectAllColumns}>Deselect all</Button> +<Button compact on:click|preventDefault={deselectAllColumns}>Deselect all</Button>
164-164: Add preventDefault to "Show more/less" button.The "Show more/less" button is also inside the form and needs the same fix.
Apply this diff:
-<Button compact on:click={() => (showAllColumns = !showAllColumns)}> +<Button compact on:click|preventDefault={() => (showAllColumns = !showAllColumns)}>
🧹 Nitpick comments (4)
src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte (1)
138-150: Consider preserving all query parameters and extracting shared URL logic.The function only preserves the
queryparameter, potentially dropping other URL search params. Additionally, this logic is duplicated inexport/+page.svelte(lines 56-68).Consider using
URLSearchParamsto preserve all query parameters:function getTableExportUrl() { - const queryParam = page.url.searchParams.get('query'); const url = resolve( '/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/export', { region: page.params.region, project: page.params.project, database: page.params.database, table: page.params.table } ); - return queryParam ? `${url}?query=${queryParam}` : url; + const params = new URLSearchParams(page.url.searchParams); + return params.size > 0 ? `${url}?${params.toString()}` : url; }For better maintainability, consider extracting this URL-building logic into a shared helper function, since the same pattern appears in
src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/export/+page.svelteat lines 56-68.src/lib/components/csvExportBox.svelte (2)
179-196: Add error handling for API calls and realtime subscription.The
migrations.list()call and realtime subscription lack error handling. If the initial load fails, users won't be notified and the UI state may be inconsistent.Consider adding error handling:
onMount(() => { sdk.forProject(page.params.region, page.params.project) .migrations.list({ queries: [ Query.equal('destination', 'CSV'), Query.equal('status', ['pending', 'processing']) ] }) .then((migrations) => { migrations.migrations.forEach(updateOrAddItem); + }) + .catch((error) => { + console.error('Failed to load CSV exports:', error); + addNotification({ + type: 'error', + message: 'Failed to load export status' + }); }); return realtime.forConsole(page.params.region, 'console', (response) => { + try { if (!response.channels.includes(`projects.${getProjectId()}`)) return; if (response.events.includes('migrations.*')) { updateOrAddItem(response.payload as Payload); } + } catch (error) { + console.error('Error processing realtime update:', error); + } }); });
89-145: Consider extracting sub-functions for better modularity.The
updateOrAddItemfunction handles multiple concerns: validation, state updates, automatic downloads, and notifications. While readable, extracting helper functions (e.g.,shouldUpdateItem,handleCompletedExport,handleFailedExport) could improve maintainability.Based on learnings
src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/export/+page.svelte (1)
56-68: Duplicate URL building logic and limited query param preservation.This URL building logic duplicates
getTableExportUrl()from+page.svelte(lines 138-150) and has the same limitation of only preserving thequeryparameter.Consider extracting this into a shared helper function that preserves all query parameters. For example, create a
getTableUrl()helper in a shared utilities file:export function getTableUrl( params: { region: string; project: string; database: string; table: string }, searchParams: URLSearchParams, suffix = '' ) { const path = suffix ? `/(console)/project-[region]-[project]/databases/database-[database]/table-[table]${suffix}` : '/(console)/project-[region]-[project]/databases/database-[database]/table-[table]'; const url = resolve(path, { region: params.region, project: params.project, database: params.database, table: params.table }); return searchParams.size > 0 ? `${url}?${searchParams.toString()}` : url; }Then use it in both files:
const tableUrl = getTableUrl(page.params, page.url.searchParams); // or const exportUrl = getTableUrl(page.params, page.url.searchParams, '/export');
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
package.json(1 hunks)src/lib/actions/analytics.ts(2 hunks)src/lib/components/csvExportBox.svelte(1 hunks)src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte(5 hunks)src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/export/+page.svelte(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- package.json
- src/lib/actions/analytics.ts
🧰 Additional context used
🧠 Learnings (6)
📚 Learning: 2025-09-30T07:41:06.679Z
Learnt from: ItzNotABug
Repo: appwrite/console PR: 2425
File: src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte:454-468
Timestamp: 2025-09-30T07:41:06.679Z
Learning: In `src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte`, the column suggestions API (console.suggestColumns) has a maximum limit of 7 columns returned, which aligns with the initial placeholder count of 7 in customColumns.
Applied to files:
src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/export/+page.sveltesrc/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte
📚 Learning: 2025-09-08T13:20:47.308Z
Learnt from: ItzNotABug
Repo: appwrite/console PR: 2316
File: src/routes/(console)/project-[region]-[project]/functions/create-function/deploy/+page.svelte:29-29
Timestamp: 2025-09-08T13:20:47.308Z
Learning: The Form.svelte component in the Appwrite console creates a FormContext with isSubmitting as writable(false) and expects consumers to work with Svelte writable stores, not plain booleans.
Applied to files:
src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/export/+page.svelte
📚 Learning: 2025-09-25T04:33:19.632Z
Learnt from: ItzNotABug
Repo: appwrite/console PR: 2373
File: src/routes/(console)/project-[region]-[project]/databases/database-[database]/createTable.svelte:28-33
Timestamp: 2025-09-25T04:33:19.632Z
Learning: In the Appwrite console createTable component, manual submit state management (like setting creatingTable = true) is not needed because: 1) The Modal component handles submit state internally via submissionLoader, preventing double submissions, and 2) After successful table creation, the entire view is destroyed and replaced with the populated table view, ending the component lifecycle.
Applied to files:
src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/export/+page.svelte
📚 Learning: 2025-11-19T11:22:42.553Z
Learnt from: atharvadeosthale
Repo: appwrite/console PR: 2512
File: src/routes/(console)/project-[region]-[project]/overview/platforms/llmBanner.svelte:51-83
Timestamp: 2025-11-19T11:22:42.553Z
Learning: In src/routes/(console)/project-[region]-[project]/overview/platforms/llmBanner.svelte, the Cursor integration URL format `https://cursor.com/link/prompt` with the `text` query parameter is correct and functional.
Applied to files:
src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte
📚 Learning: 2025-11-19T11:22:42.553Z
Learnt from: atharvadeosthale
Repo: appwrite/console PR: 2512
File: src/routes/(console)/project-[region]-[project]/overview/platforms/llmBanner.svelte:51-83
Timestamp: 2025-11-19T11:22:42.553Z
Learning: In src/routes/(console)/project-[region]-[project]/overview/platforms/llmBanner.svelte, the Lovable integration URL format `https://lovable.dev/` with `autosubmit` and `prompt` as query parameters (set via searchParams) is correct and functional.
Applied to files:
src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte
📚 Learning: 2025-10-13T05:13:54.542Z
Learnt from: ItzNotABug
Repo: appwrite/console PR: 2413
File: src/routes/(console)/project-[region]-[project]/databases/table.svelte:33-39
Timestamp: 2025-10-13T05:13:54.542Z
Learning: In Svelte 5, `import { page } from '$app/state'` provides a reactive state proxy that can be accessed directly (e.g., `page.params`), unlike the older `import { page } from '$app/stores'` which returns a readable store requiring the `$page` syntax for auto-subscription in components.
Applied to files:
src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte
🔇 Additional comments (2)
src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte (1)
203-249: LGTM!The new icon-based CSV import/export actions and expand/collapse controls are well-structured. The Export CSV button properly tracks analytics before navigation, and all buttons are correctly wrapped in Tooltips for better UX.
src/lib/components/csvExportBox.svelte (1)
132-144: Verify auto-download behavior on export completion.Line 134 automatically downloads the exported file when the migration status becomes
completed. This happens even if the user has navigated away from the export context. While the notification also provides a manual "Download" button, the automatic download might be unexpected behavior.Please confirm this is the intended UX. If not, consider removing the automatic download and relying solely on the notification's "Download" button for user-initiated downloads.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte(6 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-09-30T07:41:06.679Z
Learnt from: ItzNotABug
Repo: appwrite/console PR: 2425
File: src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte:454-468
Timestamp: 2025-09-30T07:41:06.679Z
Learning: In `src/routes/(console)/project-[region]-[project]/databases/database-[database]/(suggestions)/empty.svelte`, the column suggestions API (console.suggestColumns) has a maximum limit of 7 columns returned, which aligns with the initial placeholder count of 7 in customColumns.
Applied to files:
src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte
📚 Learning: 2025-11-19T11:22:42.553Z
Learnt from: atharvadeosthale
Repo: appwrite/console PR: 2512
File: src/routes/(console)/project-[region]-[project]/overview/platforms/llmBanner.svelte:51-83
Timestamp: 2025-11-19T11:22:42.553Z
Learning: In src/routes/(console)/project-[region]-[project]/overview/platforms/llmBanner.svelte, the Cursor integration URL format `https://cursor.com/link/prompt` with the `text` query parameter is correct and functional.
Applied to files:
src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte
📚 Learning: 2025-10-13T05:13:54.542Z
Learnt from: ItzNotABug
Repo: appwrite/console PR: 2413
File: src/routes/(console)/project-[region]-[project]/databases/table.svelte:33-39
Timestamp: 2025-10-13T05:13:54.542Z
Learning: In Svelte 5, `import { page } from '$app/state'` provides a reactive state proxy that can be accessed directly (e.g., `page.params`), unlike the older `import { page } from '$app/stores'` which returns a readable store requiring the `$page` syntax for auto-subscription in components.
Applied to files:
src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: e2e
- GitHub Check: build
🔇 Additional comments (5)
src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte (5)
5-6: LGTM!The navigation imports are correctly added to support the new CSV export functionality.
37-39: LGTM!The icon imports are correctly added and used in the template.
195-196: LGTM!The layout adjustment correctly adds spacing for the new action buttons.
207-236: LGTM!The refactored Import CSV and Export CSV buttons correctly use icon-based UI with tooltips. The Export CSV functionality properly tracks analytics and navigates to the export URL. The disabled conditions are correctly preserved from the existing button logic.
Note: The Export CSV functionality depends on the
getTableExportUrl()function, which has a URL encoding issue that should be fixed.
260-278: LGTM!The expand/collapse button correctly wraps the existing functionality in a tooltip, maintaining the same behavior while improving UX consistency with other icon buttons.
...console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (3)
src/lib/components/csvExportBox.svelte (3)
66-69: XSS vulnerability remains unresolved.The
isHtml: trueflag on line 69 combined with the unescapedtablevariable in the message creates an XSS risk, as previously flagged. User-controlled table names are rendered as HTML without sanitization.Based on past review comments, the recommended fix is to either:
- Remove
isHtml: trueand render names as plain text, or- Sanitize all user input before including in HTML strings
addNotification({ type, message, - isHtml: true, + isHtml: false, timeout: 10000,
167-179: XSS vulnerability: HTML injection via unescaped user input.The
text()function creates HTML strings with unescapedtableNameandbucketNameon lines 168-169, which are then rendered via{@html ...}on line 237. This is a critical XSS vulnerability as previously flagged.As noted in past comments, sanitize or refactor to avoid HTML string construction:
function text(status: string, tableName = '', bucketName = '') { - const table = tableName ? `<b>${tableName}</b>` : ''; - const bucket = bucketName ? `<b>${bucketName}</b>` : 'bucket'; + const table = tableName || ''; + const bucket = bucketName || 'bucket'; switch (status) { case 'completed': case 'failed': return `Export to ${bucket} ${status}`; case 'processing': return `Exporting ${table} to ${bucket}`; default: return 'Preparing export...'; } }Then in the template, use proper Svelte markup instead of
{@html}:<Typography.Text> {#if value.status === 'processing'} Exporting <b>{value.table}</b> to <b>{value.bucketName ?? 'bucket'}</b> {:else if value.status === 'completed' || value.status === 'failed'} Export to <b>{value.bucketName ?? 'bucket'}</b> {value.status} {:else} Preparing export... {/if} </Typography.Text>
237-242: Unsafe HTML rendering in template.The
{@html text(...)}directive renders unescaped user input from thetext()function, creating an XSS vulnerability. This is part of the same security issue flagged in previous comments.See the suggested fix in the review comment for lines 167-179, which shows how to refactor the template to use safe Svelte markup instead of
{@html}.
🧹 Nitpick comments (2)
src/lib/components/csvExportBox.svelte (2)
94-94: Potential undefined access in array destructuring.If
resourceId.split(':')returns fewer than 2 elements,tableIdwill beundefined. While this is handled later, the destructuring pattern[_, tableId]doesn't clearly signal this intent.- const [_, tableId] = resourceId.split(':') ?? []; + const parts = resourceId.split(':'); + const tableId = parts[1];Or use optional destructuring more explicitly:
- const [_, tableId] = resourceId.split(':') ?? []; + const tableId = resourceId.split(':')[1] ?? undefined;
153-165: Consider extracting magic numbers to named constants.The progress percentages (10, 60, 100, 30) are hardcoded. While the current implementation is clear, using named constants would improve maintainability.
const PROGRESS_PENDING = 10; const PROGRESS_PROCESSING = 60; const PROGRESS_COMPLETED = 100; const PROGRESS_DEFAULT = 30; function graphSize(status: string): number { switch (status) { case 'pending': return PROGRESS_PENDING; case 'processing': return PROGRESS_PROCESSING; case 'completed': case 'failed': return PROGRESS_COMPLETED; default: return PROGRESS_DEFAULT; } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/lib/components/csvExportBox.svelte(1 hunks)src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte(7 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: e2e
- GitHub Check: build
🔇 Additional comments (4)
src/lib/components/csvExportBox.svelte (4)
181-199: LGTM: Proper realtime subscription setup.The lifecycle correctly fetches existing migrations and sets up a realtime subscription using
realtime.forConsole(). The cleanup function is properly returned for unsubscription.
201-205: LGTM: Clean reactive state declarations.Proper use of Svelte 5 runes with
$stateand$derived. TheshowCsvExportBoxderived state efficiently tracks whether to display the component.
270-287: LGTM: Safe error details modal.The error modal properly handles JSON parsing failures and displays formatted error details. The try-catch inside the map ensures individual parse errors don't break the entire display.
289-331: LGTM: Clean scoped styles.The SCSS is well-organized with proper scoping and uses CSS custom properties for theming. The progress bar styling with
is-dangermodifier is a good pattern.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (4)
src/lib/components/csvExportBox.svelte (4)
34-49: RemoveisHtml: trueto prevent XSS.Setting
isHtml: true(line 46) witherrorMessagethat may contain unsanitized content creates an XSS vulnerability. Error messages could include table names or other user-controlled data.Apply this diff:
addNotification({ type: 'error', message: errorMessage, - isHtml: true, + isHtml: false, timeout: 10000, });Based on past review comments, this XSS concern was previously flagged.
91-96: Reconsider automatic download on completion.Automatically downloading exports on completion (line 94) may be unexpected, especially if multiple exports complete or the user navigated away. This was flagged in previous reviews as potentially intrusive.
Consider requiring explicit user action via the notification button instead:
switch (status) { case 'completed': - if (downloadUrl) { - downloadExportedFile(downloadUrl); - } + addNotification({ + type: 'success', + message: `Export of ${tableName ?? 'table'} completed`, + buttons: [{ + name: 'Download', + method: () => downloadExportedFile(downloadUrl) + }] + }); break;Based on past review comments.
121-133: SanitizetableNameor avoid HTML rendering to prevent XSS.The
text()function wrapstableNamein<b>tags without escaping, and the result is rendered via{@html ...}on line 191. IftableNamecontains HTML/script tags, this creates an XSS vulnerability.Solution 1 (preferred): Remove HTML and use Svelte's safe rendering:
In the template (line 191), replace:
-{@html text(value.status, value.table)} +{text(value.status, value.table)}Update the
text()function to return plain text:function text(status: string, tableName = '') { - const table = tableName ? `<b>${tableName}</b>` : ''; + const table = tableName || 'table'; switch (status) { case 'completed': - return `Exporting ${table} completed`; + return `Exporting "${table}" completed`;Solution 2: Add HTML escaping:
+function escapeHtml(s: string): string { + return s.replace(/[&<>"']/g, (c) => + ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c] || c) + ); +} + function text(status: string, tableName = '') { - const table = tableName ? `<b>${tableName}</b>` : ''; + const table = tableName ? `<b>${escapeHtml(tableName)}</b>` : '';Based on past review comments.
26-32: Add fallback for popup-blocked downloads.
window.open()may be blocked by popup blockers when triggered automatically (line 94). Consider using an<a>element withdownloadattribute or fallback to showing a notification with a manual download button.Apply this diff to add a fallback:
- function downloadExportedFile(downloadUrl: string) { - if (!downloadUrl) { - return; - } - - window.open(downloadUrl, '_blank'); + function downloadExportedFile(downloadUrl: string) { + if (!downloadUrl) { + return; + } + + const opened = window.open(downloadUrl, '_blank'); + if (!opened) { + addNotification({ + type: 'info', + message: 'Download ready. Click to download your CSV export.', + buttons: [{ + name: 'Download', + method: () => { + const a = document.createElement('a'); + a.href = downloadUrl; + a.download = ''; + a.click(); + } + }] + }); + } }
🧹 Nitpick comments (1)
src/lib/components/csvExportBox.svelte (1)
91-100: Consider using status constants for maintainability.Status strings like
'completed','failed','pending','processing'are used throughout (also ingraphSize, lines 67-68, 107-119). Consider defining constants to avoid typos and improve maintainability.Example:
const ExportStatus = { PENDING: 'pending', PROCESSING: 'processing', COMPLETED: 'completed', FAILED: 'failed' } as const; type ExportStatusType = typeof ExportStatus[keyof typeof ExportStatus];Then use
ExportStatus.COMPLETEDinstead of'completed'.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/lib/components/csvExportBox.svelte(1 hunks)
🧰 Additional context used
🪛 GitHub Actions: Tests
src/lib/components/csvExportBox.svelte
[warning] 1-1: Code style issues found by Prettier. Run 'prettier --write' to fix.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: e2e
🔇 Additional comments (1)
src/lib/components/csvExportBox.svelte (1)
135-153: LGTM - Real-time subscription properly implemented.The component correctly uses
realtime.forConsole()for subscriptions and fetches initial state on mount with appropriate queries.

What does this PR do?
(Provide a description of what this PR does.)
Test Plan
(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work.)
Related PRs and Issues
(If this PR is related to any other PR or resolves any issue or related to any issue link all related PR and issues here.)
Have you read the Contributing Guidelines on issues?
(Write your answer here.)
Summary by CodeRabbit
New Features
Chores
✏️ Tip: You can customize this high-level summary in your review settings.