diff --git a/src/assets/icon/cloud-download.png b/src/assets/icon/cloud-download.png
new file mode 100644
index 0000000..bb70d1b
Binary files /dev/null and b/src/assets/icon/cloud-download.png differ
diff --git a/src/assets/icon/index.js b/src/assets/icon/index.js
index 0205e8f..376130a 100644
--- a/src/assets/icon/index.js
+++ b/src/assets/icon/index.js
@@ -15,6 +15,8 @@ import QualityAsync from './total-quality-async.png';
import Failed from './total-failed.png'
import NoAvailable from './no-available.png';
+import CloudDownload from './cloud-download.png';
+
export {
OCR,
SmsAnnounce,
@@ -32,4 +34,5 @@ export {
AsyncIcon,
QualityAsync,
Failed,
+ CloudDownload
}
\ No newline at end of file
diff --git a/src/screens/Sms/Verification/Section/Announcement.jsx b/src/screens/Sms/Verification/Section/Announcement.jsx
index 43ab43a..848acbb 100644
--- a/src/screens/Sms/Verification/Section/Announcement.jsx
+++ b/src/screens/Sms/Verification/Section/Announcement.jsx
@@ -1,11 +1,632 @@
-import React from 'react'
+import React, { useState, useRef, useEffect } from 'react';
+import Select from 'react-select'
+import { ServerDownAnimation } from '../../../../assets/images';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { faImage, faTimes, faCloudUploadAlt } from '@fortawesome/free-solid-svg-icons';
+
+
+const BASE_URL = process.env.REACT_APP_BASE_URL;
+const API_KEY = process.env.REACT_APP_API_KEY;
+const fileTypes = ["image/jpeg", "image/png"];
+
+const SingleMessage = ({ applicationId, setApplicationId, messageId, setMessageId, phoneId, setPhoneId, isLoading, setIsLoading, isMobile, handleClick }) => {
+ const [inputValueApplication, setInputValueApplication] = useState('');
+ const [selectedQuota, setSelectedQuota] = useState(0);
+ const [applicationIds, setApplicationIds] = useState([]);
+ const [errorMessage, setErrorMessage] = useState('');
+ const [isServer, setIsServer] = useState(true);
+
+ const applicationOptions = applicationIds.map(app => ({
+ value: app.id, // This is what will be sent when an option is selected
+ label: app.name // This is what will be displayed in the dropdown
+ }));
+
+ const handleInputChangeApplication = (inputValue) => {
+ setInputValueApplication(inputValue);
+ };
+
+ const handleApplicationChange = (selectedOption) => {
+ const selectedId = selectedOption.value;
+ const selectedApp = applicationIds.find(app => app.id === parseInt(selectedId));
+
+ setApplicationId(selectedOption ? selectedOption.value : '');
+
+ if (selectedApp) {
+ setSelectedQuota(selectedApp.quota);
+ }
+ };
+
+ useEffect(() => {
+ const fetchApplicationIds = async () => {
+ try {
+ setIsLoading(true);
+ const response = await fetch(`${BASE_URL}/application/list`, {
+ method: 'GET',
+ headers: {
+ 'accept': 'application/json',
+ 'x-api-key': API_KEY,
+ },
+ });
+
+ if (!response.ok) {
+ throw new Error('Failed to fetch application IDs');
+ }
+
+ const data = await response.json();
+ console.log('Response Data:', data);
+
+ if (data.status_code === 200) {
+ setApplicationIds(data.details.data);
+ setIsServer(true);
+ } else {
+ setIsServer(false);
+ throw new Error(data.details.message || 'Failed to fetch application IDs');
+ }
+ } catch (error) {
+ setErrorMessage(error.message || 'Error fetching application IDs');
+ setIsServer(false);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ fetchApplicationIds();
+ }, []);
+
+ // Logic to handle the server down state should be inside the render logic, not around useEffect
+ if (!isServer) {
+ return (
+
+

+
Server tidak dapat diakses
+
{errorMessage || 'Silakan periksa koneksi internet Anda atau coba lagi nanti.'}
+
+
+ );
+ }
+
+ return (
+
+ {/* Select Application ID */}
+
+
+
+
+
+
+
+
+ Remaining Quota
+
+
+ {selectedQuota}
+ (times)
+
+
+
+
+ {/* Message Area */}
+
+
+
+
+
+ +62
+
+ setPhoneId(e.target.value)}
+ />
+
+
+
+
+ {/* Submit Button */}
+
+
+ );
+};
+
+const BulkMessage = ({ applicationId, setApplicationId, messageId, setMessageId, isMobile, isLoading, setIsLoading, handleClick }) => {
+ const fileInputRef = useRef(null);
+ const [selectedImageName, setSelectedImageName] = useState('');
+ const [errorMessage, setErrorMessage] = useState('');
+ const [inputValueApplication, setInputValueApplication] = useState('');
+ const [applicationIds, setApplicationIds] = useState([]);
+ const [selectedQuota, setSelectedQuota] = useState(0);
+ const [isServer, setIsServer] = useState(true);
+ const [file, setFile] = useState(null);
+ const [imageError, setImageError] = useState('');
+ const [validationErrors, setValidationErrors] = useState({
+ applicationId: '',
+ file: ''
+ });
+ const uploadAreaHeight = isMobile ? '50svh' : '25svh';
+
+ const applicationOptions = applicationIds.map(app => ({
+ value: app.id, // This is what will be sent when an option is selected
+ label: app.name // This is what will be displayed in the dropdown
+ }));
+
+ const handleInputChangeApplication = (inputValue) => {
+ setInputValueApplication(inputValue);
+ };
+
+ const handleApplicationChange = (selectedOption) => {
+ const selectedId = selectedOption.value;
+ const selectedApp = applicationIds.find(app => app.id === parseInt(selectedId));
+
+ setApplicationId(selectedOption ? selectedOption.value : '');
+
+ if (selectedApp) {
+ setSelectedQuota(selectedApp.quota);
+ }
+ };
+
+ const handleFileDrop = (files) => {
+ if (files && files[0]) {
+ handleImageUpload(files[0]);
+ } else {
+ console.error('No valid files dropped');
+ }
+ };
+
+ const handleImageUpload = (file) => {
+ setFile(file);
+ setSelectedImageName(file.name);
+
+ // Validate file type
+ if (!fileTypes.includes(file.type)) {
+ setImageError('Invalid file type. Only JPG, JPEG, and PNG are allowed.');
+ } else if (file.size > 2 * 1024 * 1024) { // Max 2MB
+ setImageError('File size exceeds 2MB.');
+ } else {
+ setImageError('');
+ }
+ };
+
+ const handleImageCancel = () => {
+ setFile(null);
+ setSelectedImageName('');
+ setImageError('');
+ fileInputRef.current.value = '';
+ };
+
+ useEffect(() => {
+ const fetchApplicationIds = async () => {
+ try {
+ setIsLoading(true);
+ const response = await fetch(`${BASE_URL}/application/list`, {
+ method: 'GET',
+ headers: {
+ 'accept': 'application/json',
+ 'x-api-key': API_KEY,
+ },
+ });
+
+ if (!response.ok) {
+ throw new Error('Failed to fetch application IDs');
+ }
+
+ const data = await response.json();
+ console.log('Response Data:', data);
+
+ if (data.status_code === 200) {
+ setApplicationIds(data.details.data);
+ setIsServer(true);
+ } else {
+ setIsServer(false);
+ throw new Error(data.details.message || 'Failed to fetch application IDs');
+ }
+ } catch (error) {
+ setErrorMessage(error.message || 'Error fetching application IDs');
+ setIsServer(false);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ fetchApplicationIds();
+ }, []);
+
+ // Conditional rendering should still be handled here, not affecting hook order
+ if (!isServer) {
+ return (
+
+

+
Server tidak dapat diakses
+
{errorMessage || 'Silakan periksa koneksi internet Anda atau coba lagi nanti.'}
+
+
+ );
+ }
+
+ const CustomLabel = ({ overRide, children, ...props }) => {
+ return ;
+ };
+
+ const formatFileSize = (sizeInBytes) => {
+ if (sizeInBytes < 1024) {
+ return `${sizeInBytes} bytes`; // Jika ukuran lebih kecil dari 1 KB
+ } else if (sizeInBytes < 1048576) {
+ return `${(sizeInBytes / 1024).toFixed(2)} KB`; // Jika ukuran lebih kecil dari 1 MB
+ } else {
+ return `${(sizeInBytes / 1048576).toFixed(2)} MB`; // Jika ukuran lebih besar dari 1 MB
+ }
+ };
+
+
+ return (
+
+ {/* Select Application ID */}
+
+
+
+
+
+
+
+
+ Remaining Quota
+
+
+ {selectedQuota}
+ (times)
+
+
+
+
+ {/* Message Area */}
+
+
+
+
+ {/* Upload Section */}
+
+
+
+ Upload Image (KTP)
+
+
+ {/* Drag and Drop File Input */}
+
+
+ {/* File Input */}
+
handleImageUpload(e.target.files[0])}
+ />
+
+ {selectedImageName && (
+
+
File: {selectedImageName}
+ {file && (
+
+ Size: {formatFileSize(file.size)}
+
+ )}
+
+
+ )}
+ {validationErrors.file &&
{validationErrors.file}
}
+
+
+
+
+
+ {/* Submit Button */}
+
+
+ );
+};
+
const Announcement = () => {
- return (
-
-
Sms Verification - Announcement
-
- )
-}
+ const [applicationId, setApplicationId] = useState('');
+ const [messageId, setMessageId] = useState('');
+ const [phoneId, setPhoneId] = useState('');
+ const [isLoading, setIsLoading] = useState(false);
+ const [activeTab, setActiveTab] = useState('single');
+ const [isMobile, setIsMobile] = useState(false);
-export default Announcement
+ useEffect(() => {
+ // Check if the device is mobile
+ const handleResize = () => {
+ setIsMobile(window.innerWidth <= 768);
+ };
+
+ window.addEventListener('resize', handleResize);
+ handleResize(); // Initial check
+
+ return () => {
+ window.removeEventListener('resize', handleResize);
+ };
+ }, []);
+
+ const handleClick = async () => {
+ console.log('Make SMS Demo');
+ };
+
+ return (
+
+ {isLoading && (
+
+ )}
+
+ {/* Tab buttons */}
+
+
+
+
+
+ {/* Conditional content based on active tab */}
+ {activeTab === 'single' && (
+
+ )}
+
+ {activeTab === 'bulk' && (
+
+ )}
+
+ );
+};
+
+export default Announcement;
+
+const styles = {
+ container: {
+ padding: '20px',
+ },
+ mobileContainer: {
+ padding: '10px',
+ },
+ tabContainer: {
+ marginBottom: '20px',
+ },
+ tabButton: {
+ display: 'inline-block',
+ padding: '10px 20px',
+ fontSize: '16px',
+ cursor: 'pointer',
+ backgroundColor: '#0542cc',
+ color: '#fff',
+ border: 'none',
+ marginRight: '10px',
+ borderRadius: '7px',
+ },
+ tabInactiveButton: {
+ display: 'inline-block',
+ padding: '10px 20px',
+ fontSize: '16px',
+ cursor: 'pointer',
+ backgroundColor: '#fff',
+ color: '#0542cc',
+ border: '1px solid #0542cc',
+ marginRight: '10px',
+ borderRadius: '7px',
+ },
+ customLabel: {
+ fontSize: '18px',
+ fontWeight: '600',
+ color: '#1f2d3d',
+ marginTop: '1rem'
+ },
+ selectWrapper: {
+ position: 'relative',
+ marginTop: '0',
+ },
+ select: {
+ width: '100%',
+ paddingRight: '30px',
+ flex: 1,
+ fontSize: '16px',
+ outline: 'none',
+ },
+ chevronIcon: {
+ position: 'absolute',
+ right: '10px',
+ top: '50%',
+ transform: 'translateY(-50%)',
+ pointerEvents: 'none',
+ },
+ submitButton: {
+ marginTop: '20px',
+ textAlign: 'start',
+ position: 'relative',
+ zIndex: 1,
+ },
+ uploadWrapper: {
+ marginTop: '1rem',
+ },
+ uploadArea: {
+ backgroundColor: '#e6f2ff',
+ cursor: 'pointer',
+ marginTop: '1rem',
+ padding: '3rem',
+ display: 'flex',
+ flexDirection: 'column',
+ justifyContent: 'center',
+ alignItems: 'center',
+ border: '1px solid #ced4da',
+ borderRadius: '0.25rem',
+ textAlign: 'center',
+ },
+ uploadIcon: {
+ fontSize: '40px',
+ color: '#0542cc',
+ marginBottom: '10px',
+ },
+ uploadText: {
+ color: '#1f2d3d',
+ fontWeight: '400',
+ fontSize: '16px',
+ },
+ browseLink: {
+ color: '#0542cc',
+ textDecoration: 'none',
+ fontWeight: 'bold',
+ },
+ uploadError: {
+ color: 'red',
+ fontSize: '12px',
+ marginTop: '10px',
+ },
+ remainingQuota: {
+ display: 'flex',
+ alignItems: 'center',
+ },
+ quotaText: {
+ fontSize: '24px',
+ fontWeight: '600',
+ color: '#0542cc'
+ },
+ timesText: {
+ fontSize: '16px',
+ fontWeight: '300',
+ },
+};