|
|
@@ -6,6 +6,7 @@ import {
|
|
|
User, History, Edit, MessageCircle, MessageSquare, X
|
|
|
} from 'lucide-react';
|
|
|
import LocalTime from '@/components/common/LocalTime';
|
|
|
+import api from '@/lib/api';
|
|
|
|
|
|
export interface VasTask {
|
|
|
id: number;
|
|
|
@@ -40,6 +41,9 @@ export default function TaskTable({ tasks, loading, onRetry, onManualConfirm, on
|
|
|
const [messageChannel, setMessageChannel] = useState<'sms' | 'whatsapp'>('sms');
|
|
|
const [messageTask, setMessageTask] = useState<VasTask | null>(null);
|
|
|
const [messageText, setMessageText] = useState('');
|
|
|
+ const [messageSending, setMessageSending] = useState(false);
|
|
|
+ const [messageResult, setMessageResult] = useState<'idle' | 'success' | 'error'>('idle');
|
|
|
+ const [messageResultText, setMessageResultText] = useState('');
|
|
|
|
|
|
const toggleRow = (id: number) => {
|
|
|
const newSet = new Set(expandedRows);
|
|
|
@@ -55,6 +59,9 @@ export default function TaskTable({ tasks, loading, onRetry, onManualConfirm, on
|
|
|
setMessageTask(task);
|
|
|
setMessageChannel(channel);
|
|
|
setMessageText(template);
|
|
|
+ setMessageSending(false);
|
|
|
+ setMessageResult('idle');
|
|
|
+ setMessageResultText('');
|
|
|
setIsMessageOpen(true);
|
|
|
};
|
|
|
|
|
|
@@ -63,6 +70,46 @@ export default function TaskTable({ tasks, loading, onRetry, onManualConfirm, on
|
|
|
setMessageTask(null);
|
|
|
};
|
|
|
|
|
|
+ const handleSendMessage = async () => {
|
|
|
+ if (!messageTask) return;
|
|
|
+ const target = getRecipientPhoneRaw(messageTask);
|
|
|
+ if (!target) {
|
|
|
+ setMessageResult('error');
|
|
|
+ setMessageResultText('Missing recipient phone number.');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ setMessageSending(true);
|
|
|
+ setMessageResult('idle');
|
|
|
+ setMessageResultText('');
|
|
|
+
|
|
|
+ try {
|
|
|
+ if (messageChannel === 'whatsapp') {
|
|
|
+ const res = await api.post('/api/whatsapp/send_no_token', {
|
|
|
+ chat_id: target,
|
|
|
+ message: messageText,
|
|
|
+ });
|
|
|
+ const ok = res.data?.code === 0;
|
|
|
+ setMessageResult(ok ? 'success' : 'error');
|
|
|
+ setMessageResultText(ok ? 'WhatsApp sent successfully.' : (res.data?.message || 'Failed to send WhatsApp.'));
|
|
|
+ } else {
|
|
|
+ const res = await api.post('/api/sms/send', {
|
|
|
+ send_to: target,
|
|
|
+ sender: getSmsSender(messageTask),
|
|
|
+ content: messageText,
|
|
|
+ });
|
|
|
+ const ok = res.data?.code === 0;
|
|
|
+ setMessageResult(ok ? 'success' : 'error');
|
|
|
+ setMessageResultText(ok ? 'SMS sent successfully.' : (res.data?.message || 'Failed to send SMS.'));
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ const fallback = messageChannel === 'whatsapp' ? 'Failed to send WhatsApp.' : 'Failed to send SMS.';
|
|
|
+ setMessageResult('error');
|
|
|
+ setMessageResultText(fallback);
|
|
|
+ } finally {
|
|
|
+ setMessageSending(false);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
const getRecipientName = (task: VasTask) => {
|
|
|
if (!task.user_inputs) return '-';
|
|
|
const { first_name, last_name, social_media_account } = task.user_inputs;
|
|
|
@@ -80,6 +127,13 @@ export default function TaskTable({ tasks, loading, onRetry, onManualConfirm, on
|
|
|
return combined || '-';
|
|
|
};
|
|
|
|
|
|
+ const getRecipientPhoneRaw = (task: VasTask) => {
|
|
|
+ if (!task.user_inputs) return '';
|
|
|
+ const code = String(task.user_inputs.phone_country_code || '').replace(/\D/g, '');
|
|
|
+ const rawPhone = String(task.user_inputs.phone || '').replace(/\D/g, '').replace(/^0+/, '');
|
|
|
+ return `${code}${rawPhone}`;
|
|
|
+ };
|
|
|
+
|
|
|
const getSmsSender = (task: VasTask) => {
|
|
|
return 'Visafly';
|
|
|
};
|
|
|
@@ -318,16 +372,26 @@ export default function TaskTable({ tasks, loading, onRetry, onManualConfirm, on
|
|
|
className="w-full border border-slate-200 rounded-lg p-3 text-sm text-slate-700 focus:ring-2 focus:ring-blue-500 outline-none"
|
|
|
/>
|
|
|
<p className="text-xs text-slate-400">Keep it concise (about 20 words).</p>
|
|
|
+ {messageResult !== 'idle' && (
|
|
|
+ <div className={`text-xs rounded-lg px-3 py-2 border ${
|
|
|
+ messageResult === 'success'
|
|
|
+ ? 'border-green-200 bg-green-50 text-green-700'
|
|
|
+ : 'border-red-200 bg-red-50 text-red-700'
|
|
|
+ }`}>
|
|
|
+ {messageResultText}
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
</div>
|
|
|
<div className="px-6 py-4 border-t bg-slate-50 flex justify-end gap-3">
|
|
|
<button onClick={closeMessageModal} className="px-4 py-2 border border-slate-200 rounded-lg text-slate-600 text-sm hover:bg-slate-100">
|
|
|
Cancel
|
|
|
</button>
|
|
|
<button
|
|
|
- onClick={closeMessageModal}
|
|
|
- className="px-5 py-2 bg-slate-900 text-white rounded-lg text-sm font-semibold hover:bg-slate-800"
|
|
|
+ onClick={handleSendMessage}
|
|
|
+ disabled={messageSending}
|
|
|
+ className="px-5 py-2 bg-slate-900 text-white rounded-lg text-sm font-semibold hover:bg-slate-800 disabled:opacity-60"
|
|
|
>
|
|
|
- Send
|
|
|
+ {messageSending ? 'Sending...' : 'Send'}
|
|
|
</button>
|
|
|
</div>
|
|
|
</div>
|