-
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?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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'; | ||
|
|
@@ -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 = [ | ||
|
|
@@ -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]); | ||
| } | ||
| 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) { | ||
|
|
@@ -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', | ||
|
|
@@ -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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Misleading property name: Line 318 assigns 🔎 Suggested fix optionsOption 1: Rename to 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 Verify what the #!/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 |
||
| on:remove={(e) => (files = removeFile(e.detail, files))} /> | ||
| {/if} | ||
| <Layout.Stack direction="row" justifyContent="flex-end" gap="s"> | ||
| <Button | ||
| size="s" | ||
|
|
||
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:
Repository: appwrite/console
Length of output: 254
🏁 Script executed:
Repository: appwrite/console
Length of output: 4512
🏁 Script executed:
Repository: appwrite/console
Length of output: 7796
🏁 Script executed:
Repository: appwrite/console
Length of output: 5134
🏁 Script executed:
Repository: appwrite/console
Length of output: 3588
🏁 Script executed:
Repository: appwrite/console
Length of output: 42
🏁 Script executed:
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 theUpload.Dropzonecomponent allows multiple selections and theUpload.Listcomponent 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