diff --git a/web/app/(app)/dashboard/(components)/bulk-sms-send.tsx b/web/app/(app)/dashboard/(components)/bulk-sms-send.tsx new file mode 100644 index 0000000..374f27b --- /dev/null +++ b/web/app/(app)/dashboard/(components)/bulk-sms-send.tsx @@ -0,0 +1,337 @@ +'use client' + +import { useState, useCallback, useMemo } from 'react' +import { useDropzone } from 'react-dropzone' +import Papa from 'papaparse' +import { Upload, Send, AlertCircle, CheckCircle } from 'lucide-react' +import { + Card, + CardContent, + CardHeader, + CardTitle, + CardDescription, +} from '@/components/ui/card' +import { Button } from '@/components/ui/button' +import { Input } from '@/components/ui/input' +import { Label } from '@/components/ui/label' +import { Textarea } from '@/components/ui/textarea' +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select' +import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert' +import { ApiEndpoints } from '@/config/api' +import { useMutation, useQuery } from '@tanstack/react-query' +import { Spinner } from '@/components/ui/spinner' +import httpBrowserClient from '@/lib/httpBrowserClient' + +const MAX_FILE_SIZE = 1024 * 1024 // 1 MB +const MAX_ROWS = 50 + +export default function BulkSMSSend() { + const [csvData, setCsvData] = useState([]) + const [columns, setColumns] = useState([]) + const [selectedColumn, setSelectedColumn] = useState('') + const [messageTemplate, setMessageTemplate] = useState('') + const [selectedRecipient, setSelectedRecipient] = useState('') + const [error, setError] = useState(null) + + const onDrop = useCallback((acceptedFiles: File[]) => { + const file = acceptedFiles[0] + if (file.size > MAX_FILE_SIZE) { + setError('File size exceeds 1 MB limit.') + return + } + + Papa.parse(file, { + complete: (results) => { + if (results.data && results.data.length > 0) { + if (results.data.length > MAX_ROWS) { + setError(`CSV file exceeds ${MAX_ROWS} rows limit.`) + return + } + setCsvData(results.data as any[]) + const headerRow = results.data[0] as Record + setColumns(Object.keys(headerRow)) + setError(null) + } else { + setError('CSV file is empty or invalid') + setCsvData([]) + setColumns([]) + } + }, + header: true, + skipEmptyLines: true, + }) + }, []) + + const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop }) + + const previewMessage = useMemo(() => { + if (!selectedRecipient || !messageTemplate) return '' + const recipient = csvData.find( + (row) => row[selectedColumn] === selectedRecipient + ) + if (!recipient) return '' + + return messageTemplate.replace(/\{\{\s*([^}]+)\s*\}\}/g, (_, key) => { + return recipient[key.trim()] || '' + }) + }, [selectedRecipient, messageTemplate, csvData, selectedColumn]) + + const handleSendBulkSMS = async () => { + const messages = csvData.map((row) => ({ + message: messageTemplate.replace(/\{\{\s*([^}]+)\s*\}\}/g, (_, key) => { + return row[key.trim()] || '' + }), + recipients: [row[selectedColumn]], + })) + const payload = { + messageTemplate, + messages, + } + await httpBrowserClient.post( + ApiEndpoints.gateway.sendBulkSMS(selectedDeviceId), + payload + ) + } + + const [selectedDeviceId, setSelectedDeviceId] = useState(null) + + const { data: devices } = useQuery({ + queryKey: ['devices'], + queryFn: () => + httpBrowserClient + .get(ApiEndpoints.gateway.listDevices()) + .then((res) => res.data), + }) + + const { + mutate: sendBulkSMS, + isPending: isSendingBulkSMS, + isSuccess: isSendingBulkSMSuccess, + isError: isSendingBulkSMSError, + error: sendingBulkSMSError, + } = useMutation({ + mutationFn: handleSendBulkSMS, + }) + + const isStep2Disabled = csvData.length === 0 + const isStep3Disabled = isStep2Disabled || !selectedColumn || !messageTemplate + + return ( +
+ + + Send Bulk SMS + + Upload a CSV, configure your message, and send bulk SMS in 3 simple + steps. + + + +
+

1. Upload CSV

+

+ Upload a CSV file (max 1MB, {MAX_ROWS} rows) containing recipient + information. +

+
+ + +

+ Drag & drop a CSV file here, or click to select one +

+

+ Max file size: 1MB, Max rows: 50 +

+
+ {error && ( + + + Error + {error} + + )} + {csvData.length > 0 && ( +

+ CSV uploaded successfully! {csvData.length} rows found. +

+ )} +
+ +
+

2. Configure SMS

+

+ Select the recipient column and create your message template. +

+ + {/* select device to send SMS from */} +
+ + +
+ +
+
+ + +
+
+ +