-
Notifications
You must be signed in to change notification settings - Fork 202
feat: add attachment support to support wizard #2624
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
base: main
Are you sure you want to change the base?
Conversation
Console (appwrite/console)Project ID: Tip Sites auto-generate unique domains with the pattern https://randomstring.appwrite.network |
WalkthroughAdds file-upload support to the support wizard: imports Upload and removeFile, introduces a files state (FileList | null), adds an Upload.Dropzone (maxSize 5 MB) with handleInvalid error handling and an Upload.List for displaying/removing files, and switches submission from JSON to multipart FormData (email, subject, firstName, message, tags[], customFields; conditionally attach the first file). Removes explicit Content-Type header. Emits success/error notifications, resets form data and hides the wizard on success, fetches project options on mount, and performs file/support-data cleanup on destroy. Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches🧪 Generate unit tests (beta)
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 |
|
Pending growth server release. |
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
🧹 Nitpick comments (2)
src/routes/(console)/supportWizard.svelte (2)
3-12: Attachment imports andfilesstate look good; consider cleaning up legacyfilefield.The new
Upload/removeFileimports andfiles = $state<FileList | null>(null)wiring are consistent and straightforward. One small clean‑up opportunity:$supportDatais still being reset with afileproperty in bothonDestroyandresetData, but the actual attachment state is now tracked via the localfilesvariable and sent throughFormData. Iffileon$supportDatais no longer used anywhere, consider removing it from the store shape and the resets to avoid confusion and future drift between attachment implementations.Also applies to: 37-40
115-138: FormData construction is correct; tighten null/undefined handling and clarify attachment semantics.The switch to
FormDataand the way you append scalar fields andcustomFieldsJSON looks compatible with standardmultipart/form-datahandling, and the conditionalattachmentappend based onfiles[0]is sound.Two refinements worth considering:
- For values that can be
null/undefinedat the type level (e.g.,$supportData.subject,$supportData.message, possibly$user.email), append with a safe fallback to avoid both TS complaints and accidental"null"/"undefined"strings if validation ever changes. For example:- formData.append('email', $user.email); - formData.append('subject', $supportData.subject); - formData.append('message', $supportData.message); + formData.append('email', $user.email ?? ''); + formData.append('subject', $supportData.subject ?? ''); + formData.append('message', $supportData.message ?? '');
- If
Upload.Dropzoneallows multiple files, users might expect all dropped files to be sent. Either explicitly constrain it to a single file (if supported by the component) or document/ensure that only the first file (files[0]) will ever be uploaded so behavior matches expectations.These are mostly safety/clarity improvements; the current implementation should work given the existing
requiredfields and user store.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/routes/(console)/supportWizard.svelte(5 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 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)/supportWizard.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 (1)
src/routes/(console)/supportWizard.svelte (1)
172-177: Address empty list rendering and verifyUpload.Listitem interface with the component's documentation.The list visibility issue is confirmed: an empty
FileListevaluates to truthy, so{#if files}at line 296 would render an emptyUpload.List. Update to{#if files && files.length > 0}to prevent this.For the item shape at lines 298–305, the current code passes
extension: f.type(a MIME type like"application/pdf"), but the property name suggests a file extension string. The pattern is consistent across the codebase, but the@appwrite.io/pink-sveltecomponent definition could not be accessed from the git-based package reference. Please verify the expected item interface forUpload.List(whether it expects MIME type vs. file extension, and whether any additional keys likeidortypeare needed) to ensure correctness.
ff42fcf to
f5f5883
Compare
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
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/routes/(console)/supportWizard.svelte (1)
169-170: Reset files state after successful submission.The
resetData()function resets$supportDatabut doesn't clear thefilesstate. After a successful submission, any selected files will remain visible in the UI when the wizard is reopened, creating a confusing user experience.🔎 Suggested fix to reset files state
} resetData(); + files = null; wizard.hide(); }
🧹 Nitpick comments (3)
src/routes/(console)/supportWizard.svelte (3)
187-192: Consider a more descriptive error message.The generic "Invalid file" message doesn't inform users why their file was rejected (e.g., exceeds 5MB limit, unsupported type). A more specific message would improve the user experience.
🔎 Example of more descriptive error handling
-function handleInvalid(_e: CustomEvent) { +function handleInvalid(e: CustomEvent) { + const reason = e.detail?.reason || 'file exceeds the 5MB size limit'; addNotification({ type: 'error', - message: 'Invalid file' + message: `Invalid file: ${reason}` }); }Note: Verify the structure of the
detailobject from theUpload.Dropzonecomponent to ensure correct property access.
304-310: Consider adding file type restrictions for better UX.The
Upload.Dropzonecurrently only enforces a 5MB size limit but doesn't restrict file types. Adding frontend file type validation would provide immediate feedback to users and reduce invalid submissions.🔎 Example with file type restrictions
-<Upload.Dropzone bind:files on:invalid={handleInvalid} maxSize={5 * 1024 * 1024}> +<Upload.Dropzone + bind:files + on:invalid={handleInvalid} + maxSize={5 * 1024 * 1024} + accept="image/*,.pdf,.doc,.docx,.txt,.log"> <Layout.Stack alignItems="center" gap="s"> <Typography.Text variant="l-500" >Drag and drop a file here or click to upload</Typography.Text> <Typography.Caption variant="400">Max file size: 5MB</Typography.Caption>Adjust the
acceptattribute based on what file types are appropriate for support tickets. This is optional since the backend should perform definitive validation.
103-112: Consider resetting files state in onDestroy.The
onDestroyhook resets$supportDatabut doesn't clear thefilesstate. For consistency and to prevent any potential stale state, consider also resettingfiles = nullhere.🔎 Suggested addition
onDestroy(() => { $supportData = { message: null, subject: null, category: 'technical', topic: undefined, severity: 'question', file: null }; + files = null; });
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/routes/(console)/supportWizard.svelte(5 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx,js,jsx,svelte}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx,js,jsx,svelte}: Import reusable modules from the src/lib directory using the $lib alias
Use minimal comments in code; reserve comments for TODOs or complex logic explanations
Use $lib, $routes, and $themes aliases instead of relative paths for module imports
Files:
src/routes/(console)/supportWizard.svelte
src/routes/**/*.svelte
📄 CodeRabbit inference engine (AGENTS.md)
Use SvelteKit file conventions: +page.svelte for components, +page.ts for data loaders, +layout.svelte for wrappers, +error.svelte for error handling, and dynamic route params in square brackets like [param]
Files:
src/routes/(console)/supportWizard.svelte
**/*.{ts,tsx,js,jsx,svelte,json}
📄 CodeRabbit inference engine (AGENTS.md)
Use 4 spaces for indentation, single quotes, 100 character line width, and no trailing commas per Prettier configuration
Files:
src/routes/(console)/supportWizard.svelte
**/*.svelte
📄 CodeRabbit inference engine (AGENTS.md)
Use Svelte 5 + SvelteKit 2 syntax with TypeScript for component development
Files:
src/routes/(console)/supportWizard.svelte
src/routes/**
📄 CodeRabbit inference engine (AGENTS.md)
Configure dynamic routes using SvelteKit convention with [param] syntax in route directory names
Files:
src/routes/(console)/supportWizard.svelte
🧠 Learnings (2)
📚 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)/supportWizard.svelte
📚 Learning: 2025-11-25T03:15:27.539Z
Learnt from: CR
Repo: appwrite/console PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T03:15:27.539Z
Learning: Applies to **/*.svelte : Use Svelte 5 + SvelteKit 2 syntax with TypeScript for component development
Applied to files:
src/routes/(console)/supportWizard.svelte
🔇 Additional comments (2)
src/routes/(console)/supportWizard.svelte (2)
3-12: LGTM!The new imports for
Uploadcomponent andremoveFilehelper are properly structured using the$libalias as per coding guidelines.Also applies to: 37-37
40-40: LGTM!Proper use of Svelte 5
$statesyntax with appropriateFileList | nulltyping.
| if (files && files.length > 0) { | ||
| formData.append('attachment', files[0]); | ||
| } |
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.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find all Svelte files to understand structure
find . -type f -name "*.svelte" | grep -i "upload\|dropzone" | head -20
# Search for Upload.Dropzone usage in the codebase
rg -l "Upload\.Dropzone" --type svelte
# Find where Upload is imported from
rg "import.*Upload" --type svelte | head -10Repository: appwrite/console
Length of output: 254
🏁 Script executed:
#!/bin/bash
# Search for Upload.Dropzone without file type restrictions
rg "Upload\.Dropzone" | head -20
# Also search for supportWizard.svelte file
find . -name "supportWizard.svelte" -o -name "*supportWizard*"
# Search for Upload imports
rg "from.*Upload|import.*Upload" | grep -v node_modules | head -15Repository: appwrite/console
Length of output: 4512
🏁 Script executed:
#!/bin/bash
# Read the supportWizard.svelte file to see context around lines 146-148
wc -l ./src/routes/\(console\)/supportWizard.svelte
# Read the full file to understand the context
cat -n ./src/routes/\(console\)/supportWizard.svelte | head -200Repository: appwrite/console
Length of output: 7796
🏁 Script executed:
#!/bin/bash
# Continue reading the file to see the Upload.Dropzone component
cat -n ./src/routes/\(console\)/supportWizard.svelte | sed -n '200,300p'Repository: appwrite/console
Length of output: 5134
🏁 Script executed:
#!/bin/bash
# Continue reading to find the Upload.Dropzone component
cat -n ./src/routes/\(console\)/supportWizard.svelte | sed -n '300,364p'Repository: appwrite/console
Length of output: 3588
🏁 Script executed:
#!/bin/bash
# Search for Upload.Dropzone component definition in pink-svelte
find . -path ./node_modules -prune -o -type f -name "*.ts" -o -name "*.js" -o -name "*.svelte" | xargs grep -l "export.*Dropzone" 2>/dev/null | grep -i upload | head -10
# Also check if there's any documentation or type definitions
find . -path ./node_modules -prune -o -type f \( -name "*.d.ts" -o -name "README.md" \) | xargs grep -l "Dropzone\|multiple" 2>/dev/null | head -10Repository: appwrite/console
Length of output: 42
🏁 Script executed:
#!/bin/bash
# Search for other usages of Upload.Dropzone in the codebase to see if multiple prop is ever used
rg "Upload\.Dropzone" -A 2 -B 2 | head -80
# Also check if there's a node_modules directory to check the library
ls -la node_modules/@appwrite.io/pink-svelte 2>/dev/null | head -20Repository: appwrite/console
Length of output: 8028
Only the first file is sent; restrict to single-file or send all files.
The code appends only files[0] to FormData, but the Upload.Dropzone component allows multiple selections and the Upload.List component displays all selected files with individual remove options. This creates a confusing UX where users can select and manage multiple files in the UI but only the first one is actually submitted. Either restrict the dropzone to single-file mode or modify the submission to send all selected files.
🤖 Prompt for AI Agents
In src/routes/(console)/supportWizard.svelte around lines 146-148, the form
submission only appends files[0] to FormData while the UI allows selecting
multiple files; either restrict the dropzone to single-file mode or append all
files to the FormData. To fix, choose one: A) restrict to single file by setting
the Upload.Dropzone prop to disable multiple selection (e.g., multiple={false})
and keep current append; or B) append every selected file by iterating files and
calling formData.append('attachments[]', file) (or with distinct keys) for each
entry and ensure the server expects an array of attachments. Ensure the UI,
input props, and backend contract are consistent.
| files={Array.from(files).map((f) => { | ||
| return { | ||
| ...f, | ||
| name: f.name, | ||
| size: f.size, | ||
| extension: f.type, | ||
| removable: true | ||
| }; | ||
| })} |
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.
Misleading property name: extension is assigned MIME type.
Line 318 assigns f.type (which contains the MIME type like "image/png") to a property named extension. This is misleading, as the property name suggests a file extension like ".png".
🔎 Suggested fix options
Option 1: Rename to type to reflect MIME type:
files={Array.from(files).map((f) => {
return {
...f,
name: f.name,
size: f.size,
- extension: f.type,
+ type: f.type,
removable: true
};
})}Option 2: Extract actual file extension from filename:
files={Array.from(files).map((f) => {
return {
...f,
name: f.name,
size: f.size,
- extension: f.type,
+ extension: f.name.split('.').pop() || '',
removable: true
};
})}Choose based on what the Upload.List component expects.
Verify what the Upload.List component expects for the file object structure:
#!/bin/bash
# Search for Upload.List component definition and its props
ast-grep --pattern 'export $_ List = $_'
# Check for type definitions or interfaces for Upload.List files prop
rg -n --type=ts -C5 'Upload\.List.*files'🤖 Prompt for AI Agents
In src/routes/(console)/supportWizard.svelte around lines 313 to 321, the file
mapping sets extension: f.type which stores a MIME type (e.g. "image/png") but
the property name implies a file extension; fix by making the file object match
what Upload.List expects: either rename the property to type: f.type if
Upload.List expects a MIME type, or compute the actual extension from f.name
(e.g. substring after last '.' or empty string) and assign that to extension;
verify the Upload.List file prop shape and update callers/types accordingly.

What does this PR do?
Allow users to upload a file when submitting a support ticket
Test Plan
Tested manually:
Related PRs and Issues
None
Have you read the Contributing Guidelines on issues?
Yes
Summary by CodeRabbit
New Features
Bug Fixes / UX
✏️ Tip: You can customize this high-level summary in your review settings.