mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-06 03:00:16 -04:00
fix(kb-ui): fixed upload files modal ui, processing ui to match the rest of the kb (#991)
* fix(kb-ui): fixed upload files modal, processing ui to match the rest of the kb * more ui fixes * ack PR comments * fix help modal
This commit is contained in:
@@ -178,7 +178,7 @@ export function findLocalFile(filename: string): string | null {
|
||||
* Create a file response with appropriate headers
|
||||
*/
|
||||
export function createFileResponse(file: FileResponse): NextResponse {
|
||||
return new NextResponse(file.buffer, {
|
||||
return new NextResponse(file.buffer as BodyInit, {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': file.contentType,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { Resend } from 'resend'
|
||||
import { z } from 'zod'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { env } from '@/lib/env'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { getEmailDomain } from '@/lib/urls/utils'
|
||||
@@ -9,7 +10,6 @@ const resend = env.RESEND_API_KEY ? new Resend(env.RESEND_API_KEY) : null
|
||||
const logger = createLogger('HelpAPI')
|
||||
|
||||
const helpFormSchema = z.object({
|
||||
email: z.string().email('Invalid email address'),
|
||||
subject: z.string().min(1, 'Subject is required'),
|
||||
message: z.string().min(1, 'Message is required'),
|
||||
type: z.enum(['bug', 'feedback', 'feature_request', 'other']),
|
||||
@@ -19,6 +19,15 @@ export async function POST(req: NextRequest) {
|
||||
const requestId = crypto.randomUUID().slice(0, 8)
|
||||
|
||||
try {
|
||||
// Get user session
|
||||
const session = await getSession()
|
||||
if (!session?.user?.email) {
|
||||
logger.warn(`[${requestId}] Unauthorized help request attempt`)
|
||||
return NextResponse.json({ error: 'Authentication required' }, { status: 401 })
|
||||
}
|
||||
|
||||
const email = session.user.email
|
||||
|
||||
// Check if Resend API key is configured
|
||||
if (!resend) {
|
||||
logger.error(`[${requestId}] RESEND_API_KEY not configured`)
|
||||
@@ -35,7 +44,6 @@ export async function POST(req: NextRequest) {
|
||||
const formData = await req.formData()
|
||||
|
||||
// Extract form fields
|
||||
const email = formData.get('email') as string
|
||||
const subject = formData.get('subject') as string
|
||||
const message = formData.get('message') as string
|
||||
const type = formData.get('type') as string
|
||||
@@ -47,7 +55,6 @@ export async function POST(req: NextRequest) {
|
||||
|
||||
// Validate the form data
|
||||
const result = helpFormSchema.safeParse({
|
||||
email,
|
||||
subject,
|
||||
message,
|
||||
type,
|
||||
@@ -97,9 +104,9 @@ ${message}
|
||||
}
|
||||
|
||||
// Send email using Resend
|
||||
const { data, error } = await resend.emails.send({
|
||||
from: `Sim <noreply@${getEmailDomain()}>`,
|
||||
to: [`help@${getEmailDomain()}`],
|
||||
const { error } = await resend.emails.send({
|
||||
from: `Sim <noreply@${env.EMAIL_DOMAIN || getEmailDomain()}>`,
|
||||
to: [`help@${env.EMAIL_DOMAIN || getEmailDomain()}`],
|
||||
subject: `[${type.toUpperCase()}] ${subject}`,
|
||||
replyTo: email,
|
||||
text: emailText,
|
||||
@@ -121,7 +128,7 @@ ${message}
|
||||
// Send confirmation email to the user
|
||||
await resend.emails
|
||||
.send({
|
||||
from: `Sim <noreply@${getEmailDomain()}>`,
|
||||
from: `Sim <noreply@${env.EMAIL_DOMAIN || getEmailDomain()}>`,
|
||||
to: [email],
|
||||
subject: `Your ${type} request has been received: ${subject}`,
|
||||
text: `
|
||||
@@ -137,7 +144,7 @@ ${images.length > 0 ? `You attached ${images.length} image(s).` : ''}
|
||||
Best regards,
|
||||
The Sim Team
|
||||
`,
|
||||
replyTo: `help@${getEmailDomain()}`,
|
||||
replyTo: `help@${env.EMAIL_DOMAIN || getEmailDomain()}`,
|
||||
})
|
||||
.catch((err) => {
|
||||
logger.warn(`[${requestId}] Failed to send confirmation email`, err)
|
||||
|
||||
@@ -86,7 +86,7 @@ const getStatusDisplay = (doc: DocumentData) => {
|
||||
</>
|
||||
),
|
||||
className:
|
||||
'inline-flex items-center rounded-md bg-[var(--brand-primary-hex)]/10 px-2 py-1 text-xs font-medium text-[var(--brand-primary-hex)] dark:bg-[var(--brand-primary-hex)]/20 dark:text-[var(--brand-primary-hex)]',
|
||||
'inline-flex items-center rounded-md bg-purple-100 px-2 py-1 text-xs font-medium text-[var(--brand-primary-hex)] dark:bg-purple-900/30 dark:text-[var(--brand-primary-hex)]',
|
||||
}
|
||||
case 'failed':
|
||||
return {
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/u
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Progress } from '@/components/ui/progress'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { getDocumentIcon } from '@/app/workspace/[workspaceId]/knowledge/components'
|
||||
import { useKnowledgeUpload } from '@/app/workspace/[workspaceId]/knowledge/hooks/use-knowledge-upload'
|
||||
|
||||
const logger = createLogger('UploadModal')
|
||||
@@ -152,6 +153,19 @@ export function UploadModal({
|
||||
}
|
||||
}
|
||||
|
||||
const getFileIcon = (mimeType: string, filename: string) => {
|
||||
const IconComponent = getDocumentIcon(mimeType, filename)
|
||||
return <IconComponent className='h-10 w-8' />
|
||||
}
|
||||
|
||||
const formatFileSize = (bytes: number): string => {
|
||||
if (bytes === 0) return '0 B'
|
||||
const k = 1024
|
||||
const sizes = ['B', 'KB', 'MB', 'GB']
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
||||
return `${Number.parseFloat((bytes / k ** i).toFixed(1))} ${sizes[i]}`
|
||||
}
|
||||
|
||||
// Calculate progress percentage
|
||||
const progressPercentage =
|
||||
uploadProgress.totalFiles > 0
|
||||
@@ -221,11 +235,11 @@ export function UploadModal({
|
||||
multiple
|
||||
/>
|
||||
<p className='text-sm'>
|
||||
{isDragging ? 'Drop more files here!' : 'Add more files'}
|
||||
{isDragging ? 'Drop more files here!' : 'Drop more files or click to browse'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className='max-h-60 space-y-1.5 overflow-auto'>
|
||||
<div className='max-h-60 space-y-2 overflow-auto'>
|
||||
{files.map((file, index) => {
|
||||
const fileStatus = uploadProgress.fileStatuses?.[index]
|
||||
const isCurrentlyUploading = fileStatus?.status === 'uploading'
|
||||
@@ -233,26 +247,31 @@ export function UploadModal({
|
||||
const isFailed = fileStatus?.status === 'failed'
|
||||
|
||||
return (
|
||||
<div key={index} className='space-y-1.5 rounded-md border p-2'>
|
||||
<div className='flex items-center justify-between'>
|
||||
<div key={index} className='rounded-md border p-3'>
|
||||
<div className='flex items-center gap-3'>
|
||||
{getFileIcon(file.type, file.name)}
|
||||
<div className='min-w-0 flex-1'>
|
||||
<div className='flex items-center gap-2'>
|
||||
{isCurrentlyUploading && (
|
||||
<Loader2 className='h-4 w-4 animate-spin text-blue-500' />
|
||||
<Loader2 className='h-4 w-4 animate-spin text-[var(--brand-primary-hex)]' />
|
||||
)}
|
||||
{isCompleted && <Check className='h-4 w-4 text-green-500' />}
|
||||
{isFailed && <X className='h-4 w-4 text-red-500' />}
|
||||
{!isCurrentlyUploading && !isCompleted && !isFailed && (
|
||||
<div className='h-4 w-4' />
|
||||
)}
|
||||
<p className='truncate text-sm'>
|
||||
<span className='font-medium'>{file.name}</span>
|
||||
<span className='text-muted-foreground'>
|
||||
{' '}
|
||||
• {(file.size / 1024 / 1024).toFixed(2)} MB
|
||||
</span>
|
||||
</p>
|
||||
<p className='truncate font-medium text-sm'>{file.name}</p>
|
||||
</div>
|
||||
<div className='flex items-center gap-2'>
|
||||
<p className='text-muted-foreground text-xs'>
|
||||
{formatFileSize(file.size)}
|
||||
</p>
|
||||
{isCurrentlyUploading && (
|
||||
<div className='min-w-0 max-w-32 flex-1'>
|
||||
<Progress value={fileStatus?.progress || 0} className='h-1' />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{isFailed && fileStatus?.error && (
|
||||
<p className='mt-1 text-red-500 text-xs'>{fileStatus.error}</p>
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
type='button'
|
||||
@@ -260,17 +279,11 @@ export function UploadModal({
|
||||
size='sm'
|
||||
onClick={() => removeFile(index)}
|
||||
disabled={isUploading}
|
||||
className='h-8 w-8 p-0'
|
||||
className='h-8 w-8 p-0 text-muted-foreground hover:text-destructive'
|
||||
>
|
||||
<X className='h-4 w-4' />
|
||||
</Button>
|
||||
</div>
|
||||
{isCurrentlyUploading && (
|
||||
<Progress value={fileStatus?.progress || 0} className='h-1' />
|
||||
)}
|
||||
{isFailed && fileStatus?.error && (
|
||||
<p className='text-red-500 text-xs'>{fileStatus.error}</p>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
@@ -287,7 +300,11 @@ export function UploadModal({
|
||||
<Button variant='outline' onClick={handleClose} disabled={isUploading}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={handleUpload} disabled={files.length === 0 || isUploading}>
|
||||
<Button
|
||||
onClick={handleUpload}
|
||||
disabled={files.length === 0 || isUploading}
|
||||
className='bg-[var(--brand-primary-hex)] font-[480] text-primary-foreground shadow-[0_0_0_0_var(--brand-primary-hex)] transition-all duration-200 hover:bg-[var(--brand-primary-hover-hex)] hover:shadow-[0_0_0_4px_rgba(127,47,255,0.15)]'
|
||||
>
|
||||
{isUploading
|
||||
? uploadProgress.stage === 'uploading'
|
||||
? `Uploading ${uploadProgress.filesCompleted + 1}/${uploadProgress.totalFiles}...`
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import { AlertCircle, CheckCircle2, X } from 'lucide-react'
|
||||
import { AlertCircle, X } from 'lucide-react'
|
||||
import { useParams } from 'next/navigation'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { z } from 'zod'
|
||||
@@ -109,6 +109,7 @@ export function CreateModal({ open, onOpenChange, onKnowledgeBaseCreated }: Crea
|
||||
register,
|
||||
handleSubmit,
|
||||
reset,
|
||||
watch,
|
||||
formState: { errors },
|
||||
} = useForm<FormValues>({
|
||||
resolver: zodResolver(FormSchema),
|
||||
@@ -119,9 +120,32 @@ export function CreateModal({ open, onOpenChange, onKnowledgeBaseCreated }: Crea
|
||||
maxChunkSize: 1024,
|
||||
overlapSize: 200,
|
||||
},
|
||||
mode: 'onChange',
|
||||
mode: 'onSubmit',
|
||||
})
|
||||
|
||||
// Watch the name field to enable/disable the submit button
|
||||
const nameValue = watch('name')
|
||||
|
||||
// Reset state when modal opens/closes
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
// Reset states when modal opens
|
||||
setSubmitStatus(null)
|
||||
setFileError(null)
|
||||
setFiles([])
|
||||
setIsDragging(false)
|
||||
setDragCounter(0)
|
||||
// Reset form to default values
|
||||
reset({
|
||||
name: '',
|
||||
description: '',
|
||||
minChunkSize: 1,
|
||||
maxChunkSize: 1024,
|
||||
overlapSize: 200,
|
||||
})
|
||||
}
|
||||
}, [open, reset])
|
||||
|
||||
const processFiles = async (fileList: FileList | File[]) => {
|
||||
setFileError(null)
|
||||
|
||||
@@ -292,18 +316,6 @@ export function CreateModal({ open, onOpenChange, onKnowledgeBaseCreated }: Crea
|
||||
logger.info(`Started processing ${uploadedFiles.length} documents in the background`)
|
||||
}
|
||||
|
||||
setSubmitStatus({
|
||||
type: 'success',
|
||||
message: 'Your knowledge base has been created successfully!',
|
||||
})
|
||||
reset({
|
||||
name: '',
|
||||
description: '',
|
||||
minChunkSize: 1,
|
||||
maxChunkSize: 1024,
|
||||
overlapSize: 200,
|
||||
})
|
||||
|
||||
// Clean up file previews
|
||||
files.forEach((file) => URL.revokeObjectURL(file.preview))
|
||||
setFiles([])
|
||||
@@ -313,10 +325,8 @@ export function CreateModal({ open, onOpenChange, onKnowledgeBaseCreated }: Crea
|
||||
onKnowledgeBaseCreated(newKnowledgeBase)
|
||||
}
|
||||
|
||||
// Close modal after a short delay to show success message
|
||||
setTimeout(() => {
|
||||
onOpenChange(false)
|
||||
}, 1500)
|
||||
// Close modal immediately - no need for success message
|
||||
onOpenChange(false)
|
||||
} catch (error) {
|
||||
logger.error('Error creating knowledge base:', error)
|
||||
setSubmitStatus({
|
||||
@@ -357,31 +367,13 @@ export function CreateModal({ open, onOpenChange, onKnowledgeBaseCreated }: Crea
|
||||
className='scrollbar-thin scrollbar-thumb-muted-foreground/20 hover:scrollbar-thumb-muted-foreground/25 scrollbar-track-transparent min-h-0 flex-1 overflow-y-auto px-6'
|
||||
>
|
||||
<div className='flex min-h-full flex-col py-4'>
|
||||
{submitStatus && submitStatus.type === 'success' ? (
|
||||
<Alert className='mb-6 border-border border-green-200 bg-green-50 dark:border-green-900 dark:bg-green-950/30'>
|
||||
<div className='flex items-start gap-4 py-1'>
|
||||
<div className='mt-[-1.5px] flex-shrink-0'>
|
||||
<CheckCircle2 className='h-4 w-4 text-green-600 dark:text-green-400' />
|
||||
</div>
|
||||
<div className='mr-4 flex-1 space-y-2'>
|
||||
<AlertTitle className='-mt-0.5 flex items-center justify-between'>
|
||||
<span className='font-medium text-green-600 dark:text-green-400'>
|
||||
Success
|
||||
</span>
|
||||
</AlertTitle>
|
||||
<AlertDescription className='text-green-600 dark:text-green-400'>
|
||||
{submitStatus.message}
|
||||
</AlertDescription>
|
||||
</div>
|
||||
</div>
|
||||
</Alert>
|
||||
) : submitStatus && submitStatus.type === 'error' ? (
|
||||
{submitStatus && submitStatus.type === 'error' && (
|
||||
<Alert variant='destructive' className='mb-6'>
|
||||
<AlertCircle className='h-4 w-4' />
|
||||
<AlertTitle>Error</AlertTitle>
|
||||
<AlertDescription>{submitStatus.message}</AlertDescription>
|
||||
</Alert>
|
||||
) : null}
|
||||
)}
|
||||
|
||||
{/* Form Fields Section - Fixed at top */}
|
||||
<div className='flex-shrink-0 space-y-4'>
|
||||
@@ -611,8 +603,8 @@ export function CreateModal({ open, onOpenChange, onKnowledgeBaseCreated }: Crea
|
||||
</Button>
|
||||
<Button
|
||||
type='submit'
|
||||
disabled={isSubmitting}
|
||||
className='bg-[var(--brand-primary-hex)] font-[480] text-primary-foreground shadow-[0_0_0_0_var(--brand-primary-hex)] transition-all duration-200 hover:bg-[var(--brand-primary-hover-hex)] hover:shadow-[0_0_0_4px_rgba(127,47,255,0.15)]'
|
||||
disabled={isSubmitting || !nameValue?.trim()}
|
||||
className='bg-[var(--brand-primary-hex)] font-[480] text-primary-foreground shadow-[0_0_0_0_var(--brand-primary-hex)] transition-all duration-200 hover:bg-[var(--brand-primary-hover-hex)] hover:shadow-[0_0_0_4px_rgba(127,47,255,0.15)] disabled:opacity-50 disabled:hover:shadow-none'
|
||||
>
|
||||
{isSubmitting ? 'Creating...' : 'Create Knowledge Base'}
|
||||
</Button>
|
||||
|
||||
@@ -29,9 +29,7 @@ import { createLogger } from '@/lib/logs/console/logger'
|
||||
|
||||
const logger = createLogger('HelpModal')
|
||||
|
||||
// Define form schema
|
||||
const formSchema = z.object({
|
||||
email: z.string().email('Please enter a valid email address'),
|
||||
subject: z.string().min(1, 'Subject is required'),
|
||||
message: z.string().min(1, 'Message is required'),
|
||||
type: z.enum(['bug', 'feedback', 'feature_request', 'other'], {
|
||||
@@ -77,17 +75,35 @@ export function HelpModal({ open, onOpenChange }: HelpModalProps) {
|
||||
} = useForm<FormValues>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
email: '',
|
||||
subject: '',
|
||||
message: '',
|
||||
type: 'bug', // Set default value to 'bug'
|
||||
},
|
||||
mode: 'onChange',
|
||||
mode: 'onSubmit',
|
||||
})
|
||||
|
||||
// Reset state when modal opens/closes
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
// Reset states when modal opens
|
||||
setSubmitStatus(null)
|
||||
setErrorMessage('')
|
||||
setImageError(null)
|
||||
setImages([])
|
||||
setIsDragging(false)
|
||||
setIsProcessing(false)
|
||||
// Reset form to default values
|
||||
reset({
|
||||
subject: '',
|
||||
message: '',
|
||||
type: 'bug',
|
||||
})
|
||||
}
|
||||
}, [open, reset])
|
||||
|
||||
// Listen for the custom event to open the help modal
|
||||
useEffect(() => {
|
||||
const handleOpenHelp = (event: CustomEvent) => {
|
||||
const handleOpenHelp = () => {
|
||||
onOpenChange(true)
|
||||
}
|
||||
|
||||
@@ -268,8 +284,7 @@ export function HelpModal({ open, onOpenChange }: HelpModalProps) {
|
||||
// Create FormData to handle file uploads
|
||||
const formData = new FormData()
|
||||
|
||||
// Add form fields
|
||||
formData.append('email', data.email)
|
||||
// Add form fields (email will be retrieved server-side from session)
|
||||
formData.append('subject', data.subject)
|
||||
formData.append('message', data.message)
|
||||
formData.append('type', data.type)
|
||||
@@ -377,19 +392,6 @@ export function HelpModal({ open, onOpenChange }: HelpModalProps) {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className='space-y-2'>
|
||||
<Label htmlFor='email'>Email</Label>
|
||||
<Input
|
||||
id='email'
|
||||
placeholder='your.email@example.com'
|
||||
{...register('email')}
|
||||
className={`h-9 rounded-[8px] ${errors.email ? 'border-red-500' : ''}`}
|
||||
/>
|
||||
{errors.email && (
|
||||
<p className='mt-1 text-red-500 text-sm'>{errors.email.message}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className='space-y-2'>
|
||||
<Label htmlFor='subject'>Subject</Label>
|
||||
<Input
|
||||
@@ -408,7 +410,7 @@ export function HelpModal({ open, onOpenChange }: HelpModalProps) {
|
||||
<Textarea
|
||||
id='message'
|
||||
placeholder='Please provide details about your request...'
|
||||
rows={5}
|
||||
rows={6}
|
||||
{...register('message')}
|
||||
className={`rounded-[8px] ${errors.message ? 'border-red-500' : ''}`}
|
||||
/>
|
||||
@@ -426,9 +428,10 @@ export function HelpModal({ open, onOpenChange }: HelpModalProps) {
|
||||
onDragOver={handleDragOver}
|
||||
onDragLeave={handleDragLeave}
|
||||
onDrop={handleDrop}
|
||||
className={`flex items-center gap-4 ${
|
||||
isDragging ? 'rounded-md bg-primary/5 p-2' : ''
|
||||
className={`cursor-pointer rounded-lg border-2 border-muted-foreground/25 border-dashed p-6 text-center transition-colors hover:bg-muted/50 ${
|
||||
isDragging ? 'border-primary bg-primary/5' : ''
|
||||
}`}
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
>
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
@@ -438,17 +441,12 @@ export function HelpModal({ open, onOpenChange }: HelpModalProps) {
|
||||
className='hidden'
|
||||
multiple
|
||||
/>
|
||||
<Button
|
||||
type='button'
|
||||
variant='outline'
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
className='flex h-9 items-center justify-center gap-2 rounded-[8px]'
|
||||
>
|
||||
<Upload className='h-4 w-4' />
|
||||
Upload Images
|
||||
</Button>
|
||||
<p className='text-muted-foreground text-xs'>
|
||||
Drop images here or click to upload. Max 20MB per image.
|
||||
<Upload className='mx-auto mb-2 h-8 w-8 text-muted-foreground' />
|
||||
<p className='text-sm'>
|
||||
{isDragging ? 'Drop images here!' : 'Drop images here or click to browse'}
|
||||
</p>
|
||||
<p className='mt-1 text-muted-foreground text-xs'>
|
||||
JPEG, PNG, WebP, GIF (max 20MB each)
|
||||
</p>
|
||||
</div>
|
||||
{imageError && <p className='mt-1 text-red-500 text-sm'>{imageError}</p>}
|
||||
@@ -494,18 +492,13 @@ export function HelpModal({ open, onOpenChange }: HelpModalProps) {
|
||||
{/* Overlay Footer */}
|
||||
<div className='absolute inset-x-0 bottom-0 bg-background'>
|
||||
<div className='flex w-full items-center justify-between px-6 py-4'>
|
||||
<Button
|
||||
variant='outline'
|
||||
onClick={handleClose}
|
||||
type='button'
|
||||
className='h-9 rounded-[8px]'
|
||||
>
|
||||
<Button variant='outline' onClick={handleClose} type='button'>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
type='submit'
|
||||
disabled={isSubmitting || isProcessing}
|
||||
className='h-9 rounded-[8px]'
|
||||
className='bg-[var(--brand-primary-hex)] font-[480] text-primary-foreground shadow-[0_0_0_0_var(--brand-primary-hex)] transition-all duration-200 hover:bg-[var(--brand-primary-hover-hex)] hover:shadow-[0_0_0_4px_rgba(127,47,255,0.15)] disabled:opacity-50 disabled:hover:shadow-none'
|
||||
>
|
||||
{isSubmitting ? 'Submitting...' : 'Submit'}
|
||||
</Button>
|
||||
|
||||
@@ -1167,9 +1167,9 @@ export const auth = betterAuth({
|
||||
stripeClient,
|
||||
stripeWebhookSecret: env.STRIPE_WEBHOOK_SECRET || '',
|
||||
createCustomerOnSignUp: true,
|
||||
onCustomerCreate: async ({ customer, stripeCustomer, user }, request) => {
|
||||
onCustomerCreate: async ({ stripeCustomer, user }, request) => {
|
||||
logger.info('Stripe customer created', {
|
||||
customerId: customer.id,
|
||||
customerId: stripeCustomer.id,
|
||||
userId: user.id,
|
||||
})
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@ describe('Email Validation', () => {
|
||||
it.concurrent('should accept legitimate business emails', async () => {
|
||||
const legitimateEmails = [
|
||||
'test@gmail.com',
|
||||
'noreply@gmail.com',
|
||||
'no-reply@yahoo.com',
|
||||
'user12345@outlook.com',
|
||||
'longusernamehere@gmail.com',
|
||||
|
||||
Reference in New Issue
Block a user