Browse Source
Merge pull request #117 from vernu/improve-support-form
Merge pull request #117 from vernu/improve-support-form
Improve customer-support formpull/118/head
committed by
GitHub
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 10 additions and 260 deletions
-
9web/app/(app)/dashboard/(components)/support-form.tsx
-
257web/components/shared/customer-support.tsx
-
4web/lib/auth.ts
@ -1,257 +0,0 @@ |
|||||
'use client' |
|
||||
|
|
||||
import { Button } from '@/components/ui/button' |
|
||||
import { |
|
||||
Dialog, |
|
||||
DialogContent, |
|
||||
DialogDescription, |
|
||||
DialogHeader, |
|
||||
DialogTitle, |
|
||||
DialogTrigger, |
|
||||
} from '@/components/ui/dialog' |
|
||||
import { |
|
||||
Form, |
|
||||
FormControl, |
|
||||
FormField, |
|
||||
FormItem, |
|
||||
FormLabel, |
|
||||
FormMessage, |
|
||||
} from '@/components/ui/form' |
|
||||
import { Input } from '@/components/ui/input' |
|
||||
import { |
|
||||
Select, |
|
||||
SelectContent, |
|
||||
SelectItem, |
|
||||
SelectTrigger, |
|
||||
SelectValue, |
|
||||
} from '@/components/ui/select' |
|
||||
import { Textarea } from '@/components/ui/textarea' |
|
||||
import { AlertTriangle, Check, Loader2, MessageSquarePlus } from 'lucide-react' |
|
||||
import { useState } from 'react' |
|
||||
import { useForm } from 'react-hook-form' |
|
||||
import { z } from 'zod' |
|
||||
import { zodResolver } from '@hookform/resolvers/zod' |
|
||||
import { toast } from '@/hooks/use-toast' |
|
||||
import httpBrowserClient from '@/lib/httpBrowserClient' |
|
||||
import { ApiEndpoints } from '@/config/api' |
|
||||
|
|
||||
const SupportFormSchema = z.object({ |
|
||||
name: z.string().min(1, { message: 'Name is required' }), |
|
||||
email: z.string().email({ message: 'Invalid email address' }), |
|
||||
phone: z.string().optional(), |
|
||||
category: z.enum(['general', 'technical', 'billing-and-payments', 'other'], { |
|
||||
message: 'Support category is required', |
|
||||
}), |
|
||||
message: z.string().min(1, { message: 'Message is required' }), |
|
||||
}) |
|
||||
|
|
||||
export default function SupportButton() { |
|
||||
const [open, setOpen] = useState(false) |
|
||||
const [isSubmitting, setIsSubmitting] = useState(false) |
|
||||
const [isSubmitSuccessful, setIsSubmitSuccessful] = useState(false) |
|
||||
const [errorMessage, setErrorMessage] = useState<string | null>(null) |
|
||||
|
|
||||
const form = useForm({ |
|
||||
resolver: zodResolver(SupportFormSchema), |
|
||||
defaultValues: { |
|
||||
name: '', |
|
||||
email: '', |
|
||||
phone: '', |
|
||||
category: 'general', |
|
||||
message: '', |
|
||||
}, |
|
||||
}) |
|
||||
|
|
||||
const onSubmit = async (data: any) => { |
|
||||
setIsSubmitting(true) |
|
||||
setErrorMessage(null) |
|
||||
|
|
||||
try { |
|
||||
// Use the existing httpBrowserClient to call the NestJS endpoint
|
|
||||
const response = await httpBrowserClient.post( |
|
||||
ApiEndpoints.support.customerSupport(), |
|
||||
data |
|
||||
) |
|
||||
|
|
||||
setIsSubmitSuccessful(true) |
|
||||
|
|
||||
toast({ |
|
||||
title: 'Support request submitted', |
|
||||
description: response.data.message || 'We will get back to you soon.', |
|
||||
}) |
|
||||
|
|
||||
// Wait 3 seconds before closing the dialog
|
|
||||
setTimeout(() => { |
|
||||
setOpen(false) |
|
||||
}, 3000) |
|
||||
} catch (error) { |
|
||||
console.error('Error submitting support request:', error) |
|
||||
|
|
||||
setErrorMessage( |
|
||||
'Error submitting support request. Please try again later.' |
|
||||
) |
|
||||
|
|
||||
toast({ |
|
||||
title: 'Error submitting support request', |
|
||||
description: 'Please try again later', |
|
||||
variant: 'destructive', |
|
||||
}) |
|
||||
} finally { |
|
||||
setIsSubmitting(false) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
const onOpenChange = (open: boolean) => { |
|
||||
setOpen(open) |
|
||||
if (!open) { |
|
||||
form.reset() |
|
||||
setIsSubmitSuccessful(false) |
|
||||
setErrorMessage(null) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return ( |
|
||||
<Dialog open={open} onOpenChange={onOpenChange}> |
|
||||
<DialogTrigger asChild> |
|
||||
<Button |
|
||||
className='fixed bottom-4 right-4 shadow-lg bg-brand-500 hover:bg-brand-600 dark:text-white rounded-full' |
|
||||
size='sm' |
|
||||
> |
|
||||
<MessageSquarePlus className='h-5 w-5 mr-1' /> |
|
||||
<span className='mr-1'>Support</span> |
|
||||
</Button> |
|
||||
</DialogTrigger> |
|
||||
<DialogContent> |
|
||||
<DialogHeader> |
|
||||
<DialogTitle>Contact Support</DialogTitle> |
|
||||
<DialogDescription> |
|
||||
Fill out the form below and we'll get back to you as soon as |
|
||||
possible. |
|
||||
</DialogDescription> |
|
||||
</DialogHeader> |
|
||||
<Form {...form}> |
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className='space-y-4'> |
|
||||
<FormField |
|
||||
control={form.control} |
|
||||
name='category' |
|
||||
disabled={isSubmitting} |
|
||||
render={({ field }) => ( |
|
||||
<FormItem> |
|
||||
<FormLabel>Support Category</FormLabel> |
|
||||
<Select |
|
||||
onValueChange={field.onChange} |
|
||||
disabled={isSubmitting} |
|
||||
defaultValue={field.value} |
|
||||
> |
|
||||
<FormControl> |
|
||||
<SelectTrigger> |
|
||||
<SelectValue placeholder='Select support category' /> |
|
||||
</SelectTrigger> |
|
||||
</FormControl> |
|
||||
<SelectContent> |
|
||||
<SelectItem value='general'>General Inquiry</SelectItem> |
|
||||
<SelectItem value='technical'> |
|
||||
Technical Support |
|
||||
</SelectItem> |
|
||||
<SelectItem value='billing-and-payments'> |
|
||||
Billing and Payments |
|
||||
</SelectItem> |
|
||||
<SelectItem value='other'>Other</SelectItem> |
|
||||
</SelectContent> |
|
||||
</Select> |
|
||||
<FormMessage /> |
|
||||
</FormItem> |
|
||||
)} |
|
||||
/> |
|
||||
<FormField |
|
||||
control={form.control} |
|
||||
name='name' |
|
||||
disabled={isSubmitting} |
|
||||
render={({ field }) => ( |
|
||||
<FormItem> |
|
||||
<FormLabel>Name</FormLabel> |
|
||||
<FormControl> |
|
||||
<Input placeholder='Your name' {...field} /> |
|
||||
</FormControl> |
|
||||
<FormMessage /> |
|
||||
</FormItem> |
|
||||
)} |
|
||||
/> |
|
||||
<FormField |
|
||||
control={form.control} |
|
||||
name='email' |
|
||||
disabled={isSubmitting} |
|
||||
render={({ field }) => ( |
|
||||
<FormItem> |
|
||||
<FormLabel>Email</FormLabel> |
|
||||
<FormControl> |
|
||||
<Input |
|
||||
placeholder='your@email.com' |
|
||||
type='email' |
|
||||
{...field} |
|
||||
/> |
|
||||
</FormControl> |
|
||||
<FormMessage /> |
|
||||
</FormItem> |
|
||||
)} |
|
||||
/> |
|
||||
<FormField |
|
||||
control={form.control} |
|
||||
name='phone' |
|
||||
disabled={isSubmitting} |
|
||||
render={({ field }) => ( |
|
||||
<FormItem> |
|
||||
<FormLabel>Phone (Optional)</FormLabel> |
|
||||
<FormControl> |
|
||||
<Input placeholder='+1234567890' type='tel' {...field} /> |
|
||||
</FormControl> |
|
||||
<FormMessage /> |
|
||||
</FormItem> |
|
||||
)} |
|
||||
/> |
|
||||
<FormField |
|
||||
control={form.control} |
|
||||
name='message' |
|
||||
disabled={isSubmitting} |
|
||||
render={({ field }) => ( |
|
||||
<FormItem> |
|
||||
<FormLabel>Message</FormLabel> |
|
||||
<FormControl> |
|
||||
<Textarea |
|
||||
placeholder='How can we help you?' |
|
||||
className='min-h-[100px]' |
|
||||
{...field} |
|
||||
/> |
|
||||
</FormControl> |
|
||||
<FormMessage /> |
|
||||
</FormItem> |
|
||||
)} |
|
||||
/> |
|
||||
{isSubmitSuccessful && ( |
|
||||
<div className='flex items-center gap-2 text-green-500'> |
|
||||
<Check className='h-4 w-4' /> We have received your message, we |
|
||||
will get back to you soon. |
|
||||
</div> |
|
||||
)} |
|
||||
|
|
||||
{errorMessage && ( |
|
||||
<div className='flex items-center gap-2 text-red-500'> |
|
||||
<AlertTriangle className='h-4 w-4' /> {errorMessage} |
|
||||
</div> |
|
||||
)} |
|
||||
<Button type='submit' disabled={isSubmitting} className='w-full'> |
|
||||
{isSubmitting ? ( |
|
||||
<> |
|
||||
<Loader2 className='h-4 w-4 animate-spin mr-2' />{' '} |
|
||||
Submitting... |
|
||||
</> |
|
||||
) : ( |
|
||||
'Submit' |
|
||||
)} |
|
||||
</Button> |
|
||||
</form> |
|
||||
</Form> |
|
||||
</DialogContent> |
|
||||
</Dialog> |
|
||||
) |
|
||||
} |
|
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue