Skip to content
Open
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
78 changes: 60 additions & 18 deletions src/routes/(console)/supportWizard.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
<script lang="ts">
import { Wizard } from '$lib/layout';
import { Icon, Input, Layout, Popover, Tag, Typography, Card } from '@appwrite.io/pink-svelte';
import {
Icon,
Input,
Layout,
Popover,
Tag,
Typography,
Card,
Upload
} from '@appwrite.io/pink-svelte';
import { supportData, isSupportOnline } from './wizard/support/store';
import { onMount, onDestroy } from 'svelte';
import { sdk } from '$lib/stores/sdk';
Expand All @@ -25,8 +34,10 @@
import { wizard } from '$lib/stores/wizard';
import { VARS } from '$lib/system';
import { IconCheckCircle, IconXCircle, IconInfo } from '@appwrite.io/pink-icons-svelte';
import { removeFile } from '$lib/helpers/files';
let projectOptions = $state<Array<{ value: string; label: string }>>([]);
let files = $state<FileList | null>(null);
// Category options with display names
const categories = [
Expand Down Expand Up @@ -116,25 +127,29 @@
? `${$supportData.category}-${$supportData.topic}`.toLowerCase()
: $supportData.category.toLowerCase();
const formData = new FormData();
formData.append('email', $user.email);
formData.append('subject', $supportData.subject);
formData.append('firstName', ($user?.name || 'Unknown').slice(0, 40));
formData.append('message', $supportData.message);
formData.append('tags[]', categoryTopicTag);
formData.append(
'customFields',
JSON.stringify([
{ id: '41612', value: $supportData.category },
{ id: '48492', value: $organization?.$id ?? '' },
{ id: '48491', value: $supportData?.project ?? '' },
{ id: '56023', value: $supportData?.severity ?? '' },
{ id: '56024', value: $organization?.billingPlan ?? '' }
])
);
if (files && files.length > 0) {
formData.append('attachment', files[0]);
}
Comment on lines +146 to +148
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 | 🟠 Major

🧩 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 -10

Repository: 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 -15

Repository: 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 -200

Repository: 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 -10

Repository: 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 -20

Repository: 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.

const response = await fetch(`${VARS.GROWTH_ENDPOINT}/support`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
email: $user.email,
subject: $supportData.subject,
firstName: ($user?.name || 'Unknown').slice(0, 40),
message: $supportData.message,
tags: [categoryTopicTag],
customFields: [
{ id: '41612', value: $supportData.category },
{ id: '48492', value: $organization?.$id ?? '' },
{ id: '48491', value: $supportData?.project ?? '' },
{ id: '56023', value: $supportData?.severity ?? '' },
{ id: '56024', value: $organization?.billingPlan ?? '' }
]
})
body: formData
});
trackEvent(Submit.SupportTicket);
if (response.status !== 200) {
Expand Down Expand Up @@ -169,6 +184,13 @@
$wizard.finalAction = handleSubmit;
function handleInvalid(_e: CustomEvent) {
addNotification({
type: 'error',
message: 'Invalid file'
});
}
const workTimings = {
start: '16:00',
end: '00:00',
Expand Down Expand Up @@ -279,6 +301,26 @@
label="Tell us a bit more"
required
maxlength={4096} />
<Upload.Dropzone bind:files on:invalid={handleInvalid} maxSize={5 * 1024 * 1024}>
<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>
</Layout.Stack>
</Upload.Dropzone>
{#if files}
<Upload.List
files={Array.from(files).map((f) => {
return {
...f,
name: f.name,
size: f.size,
extension: f.type,
removable: true
};
})}
Comment on lines +313 to +321
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 | 🟡 Minor

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.

on:remove={(e) => (files = removeFile(e.detail, files))} />
{/if}
<Layout.Stack direction="row" justifyContent="flex-end" gap="s">
<Button
size="s"
Expand Down