update params customer_phone on module notification
This commit is contained in:
@ -26,9 +26,10 @@ import { Button } from '@/components/ui/button';
|
||||
import { useManageNotificationContext } from '../hooks/useManageNotificationContext';
|
||||
import { doSaveLogActivity } from '@/actions/GlobalActions';
|
||||
import { CustomerProps } from '@/pages/master/provider/blocks/AddDialog';
|
||||
import { Check, ChevronDown } from 'lucide-react';
|
||||
import { Check, ChevronDown, X } from 'lucide-react';
|
||||
import { initialStateNotification, selectedNotification, validateFormNotification } from './Types';
|
||||
import { useDebounce } from './useDebounce';
|
||||
import Papa from 'papaparse';
|
||||
|
||||
const API_URL_CUSTOMER = apiConfig.service_customer;
|
||||
const API_URL_NOTIFICATION = apiConfig.service_notification;
|
||||
@ -42,16 +43,23 @@ const AddDialog = () => {
|
||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const debouncedSearchQuery = useDebounce(searchQuery, 500);
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const [formField, setFormField] = useState<selectedNotification>(initialStateNotification);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [customers, setCustomers] = useState<CustomerProps[]>([]);
|
||||
const [uploadedFileName, setUploadedFileName] = useState<string>('');
|
||||
|
||||
const resetForm = () => {
|
||||
setFormField({ ...initialStateNotification });
|
||||
setIsSubmitting(false);
|
||||
setAlert({ show: false, message: '' });
|
||||
setErrors({});
|
||||
setUploadedFileName('');
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.value = '';
|
||||
}
|
||||
};
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
||||
@ -79,7 +87,6 @@ const AddDialog = () => {
|
||||
|
||||
try {
|
||||
const response = await PostData(`${API_URL_NOTIFICATION}/send`, formField);
|
||||
// console.log('send response :', response);
|
||||
|
||||
if (response?.status) {
|
||||
handleAddDialog(false);
|
||||
@ -148,6 +155,90 @@ const AddDialog = () => {
|
||||
}
|
||||
}, [showAddDialog]);
|
||||
|
||||
const handleFileUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = event.target.files?.[0];
|
||||
|
||||
if (!file) return;
|
||||
|
||||
// Validasi tipe file
|
||||
if (!file.name.endsWith('.csv')) {
|
||||
toast.error('Please upload a CSV file');
|
||||
return;
|
||||
}
|
||||
|
||||
setUploadedFileName(file.name);
|
||||
|
||||
Papa.parse(file, {
|
||||
header: true,
|
||||
skipEmptyLines: true,
|
||||
dynamicTyping: false,
|
||||
complete: (results) => {
|
||||
try {
|
||||
const phoneNumbers: string[] = [];
|
||||
|
||||
// Extract phone numbers from CSV
|
||||
results.data.forEach((row: any) => {
|
||||
// Cari kolom yang berisi nomor telepon (bisa MSISDN, phone, atau nama kolom lain)
|
||||
const phone = row.MSISDN || row.msisdn || row.phone || row.Phone || row.PHONE;
|
||||
|
||||
if (phone) {
|
||||
// Bersihkan nomor telepon dari karakter non-digit
|
||||
const cleanPhone = String(phone).replace(/\D/g, '');
|
||||
|
||||
if (cleanPhone && cleanPhone.length >= 10) {
|
||||
phoneNumbers.push(cleanPhone);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (phoneNumbers.length === 0) {
|
||||
toast.error('No valid phone numbers found in CSV. Please check column name (MSISDN)');
|
||||
setUploadedFileName('');
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.value = '';
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Update form field dengan phone numbers
|
||||
setFormField((prev) => ({
|
||||
...prev,
|
||||
customer_phone: phoneNumbers
|
||||
}));
|
||||
|
||||
toast.success(`${phoneNumbers.length} phone numbers extracted successfully`);
|
||||
} catch (error) {
|
||||
console.error('Error parsing CSV:', error);
|
||||
toast.error('Failed to parse CSV file');
|
||||
setUploadedFileName('');
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.value = '';
|
||||
}
|
||||
}
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error reading CSV:', error);
|
||||
toast.error('Error reading CSV file');
|
||||
setUploadedFileName('');
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.value = '';
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleRemoveFile = () => {
|
||||
setFormField((prev) => ({
|
||||
...prev,
|
||||
customer_phone: []
|
||||
}));
|
||||
setUploadedFileName('');
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.value = '';
|
||||
}
|
||||
toast.info('Phone numbers cleared');
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={showAddDialog} onOpenChange={handleAddDialog}>
|
||||
<DialogContent className="container-fixed max-w-[768px] flex flex-col p-5 overflow-hidden">
|
||||
@ -358,6 +449,7 @@ const AddDialog = () => {
|
||||
checked={formField.via === 'whatsapp'}
|
||||
onChange={handleChange}
|
||||
className="accent-blue-600"
|
||||
disabled
|
||||
/>
|
||||
<span>WhatsApp</span>
|
||||
</label>
|
||||
@ -372,6 +464,38 @@ const AddDialog = () => {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="w-full">
|
||||
<div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
|
||||
<label className="form-label flex items-center gap-1 max-w-56">
|
||||
Upload MSISDN CSV
|
||||
</label>
|
||||
<div className="flex-1 flex flex-col gap-2">
|
||||
<Input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
accept=".csv"
|
||||
onChange={handleFileUpload}
|
||||
className="input col-span-6"
|
||||
/>
|
||||
{uploadedFileName && formField.customer_phone.length > 0 && (
|
||||
<div className="flex items-center gap-2 text-sm text-green-600 bg-green-50 px-3 py-2 rounded-md">
|
||||
<Check className="w-4 h-4" />
|
||||
<span>
|
||||
{uploadedFileName} - {formField.customer_phone.length} phone numbers
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleRemoveFile}
|
||||
className="ml-auto text-red-500 hover:text-red-700"
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full">
|
||||
<div className="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
|
||||
<label className="form-label flex items-center gap-1 max-w-56">
|
||||
@ -381,7 +505,7 @@ const AddDialog = () => {
|
||||
className="input col-span-6"
|
||||
name="content"
|
||||
placeholder="Enter OTP (5 digits)"
|
||||
value={formField.content}
|
||||
// value={formField.content}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value;
|
||||
if (/^\d{0,5}$/.test(value)) {
|
||||
|
||||
@ -7,6 +7,7 @@ export interface selectedNotification {
|
||||
via: string;
|
||||
subject: string;
|
||||
content: string;
|
||||
customer_phone: string[];
|
||||
}
|
||||
|
||||
export const initialStateNotification: selectedNotification = {
|
||||
@ -15,7 +16,8 @@ export const initialStateNotification: selectedNotification = {
|
||||
type: '',
|
||||
via: '',
|
||||
subject: '',
|
||||
content: ''
|
||||
content: '',
|
||||
customer_phone: []
|
||||
};
|
||||
|
||||
export const validateFormNotification = (
|
||||
@ -47,8 +49,8 @@ export const validateFormNotification = (
|
||||
// Validasi khusus content
|
||||
if (formField.via === 'whatsapp') {
|
||||
if (!formField.content || !/^\d{5}$/.test(formField.content)) {
|
||||
newErrors.content = 'Content harus berupa 5 digit angka (OTP)';
|
||||
toast.error('Content harus berupa 5 digit angka (OTP)');
|
||||
newErrors.content = 'Content must be a 5-digit number (OTP)';
|
||||
toast.error('Content must be a 5-digit number (OTP)');
|
||||
isValid = false;
|
||||
}
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user