diff --git a/package-lock.json b/package-lock.json
index a7c2c54..5567459 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -19,6 +19,7 @@
"bootstrap": "^5.3.3",
"font-awesome": "^4.7.0",
"react": "^18.3.1",
+ "react-copy-to-clipboard": "^5.1.0",
"react-dom": "^18.3.1",
"react-drag-drop-files": "^2.4.0",
"react-icons": "^5.3.0",
@@ -5973,6 +5974,15 @@
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
},
+ "node_modules/copy-to-clipboard": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz",
+ "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==",
+ "license": "MIT",
+ "dependencies": {
+ "toggle-selection": "^1.0.6"
+ }
+ },
"node_modules/core-js": {
"version": "3.39.0",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.39.0.tgz",
@@ -13344,6 +13354,19 @@
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
},
+ "node_modules/react-copy-to-clipboard": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.1.0.tgz",
+ "integrity": "sha512-k61RsNgAayIJNoy9yDsYzDe/yAZAzEbEgcz3DZMhF686LEyukcE1hzurxe85JandPUG+yTfGVFzuEw3xt8WP/A==",
+ "license": "MIT",
+ "dependencies": {
+ "copy-to-clipboard": "^3.3.1",
+ "prop-types": "^15.8.1"
+ },
+ "peerDependencies": {
+ "react": "^15.3.0 || 16 || 17 || 18"
+ }
+ },
"node_modules/react-dev-utils": {
"version": "12.0.1",
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz",
@@ -15541,6 +15564,12 @@
"node": ">=8.0"
}
},
+ "node_modules/toggle-selection": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
+ "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==",
+ "license": "MIT"
+ },
"node_modules/toidentifier": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
diff --git a/package.json b/package.json
index ba400aa..672eaf4 100644
--- a/package.json
+++ b/package.json
@@ -14,6 +14,7 @@
"bootstrap": "^5.3.3",
"font-awesome": "^4.7.0",
"react": "^18.3.1",
+ "react-copy-to-clipboard": "^5.1.0",
"react-dom": "^18.3.1",
"react-drag-drop-files": "^2.4.0",
"react-icons": "^5.3.0",
diff --git a/src/App.js b/src/App.js
index 58420ca..68565d2 100644
--- a/src/App.js
+++ b/src/App.js
@@ -13,53 +13,101 @@ import {
CreateApps
} from './screens/Home';
import {
- FaceVerify,
- FaceSummary,
- FaceTransaction
+ Verify as FaceVerify,
+ Summary as FaceSummary,
+ Transaction as FaceTransaction
} from './screens/Biometric/FaceRecognition';
import {
Enroll,
- VerifySection,
+ Verify as VerifySection,
Liveness,
Compare,
Search
} from './screens/Biometric/FaceRecognition/Section';
import {
- ManageKtp,
- SummaryKtp,
- VerifyKtp,
- TransactionKtp
+ Manage as ManageKtp,
+ Summary as SummaryKtp,
+ Verify as VerifyKtp,
+ Transaction as TransactionKtp
} from './screens/Biometric/OcrKtp';
import {
- SummaryNpwp,
- TransactionNpwp,
- VerifyNpwp
+ Summary as SummaryNpwp,
+ Transaction as TransactionNpwp,
+ Verify as VerifyNpwp
} from './screens/Biometric/OcrNpwp';
import {
- VerifySim,
- SummarySim,
- TransactionSim
+ Verify as VerifySim,
+ Summary as SummarySim,
+ Transaction as TransactionSim
} from './screens/Biometric/OcrSim';
import {
- VerifyDoc,
- SummaryDoc,
- TransactionDoc
+ Verify as VerifyDoc,
+ Summary as SummaryDoc,
+ Transaction as TransactionDoc
} from './screens/Biometric/OcrDocument';
import {
- SmsVerify
+ Verify as SmsVerify
} from './screens/Sms/Verification';
import {
- VerificationAnnoncement,
- VerificationOtp
+ Annoncement as VerificationAnnoncement,
+ Otp as VerificationOtp
} from './screens/Sms/Verification/Section';
+import {
+ Settings as SmsOtpSettings,
+ Summary as SmsOtpSummary,
+ Transaction as SmsOtpTransaction,
+ Detail as SmsOtpDetail
+} from './screens/Sms/OtpManagement';
+
+import {
+ Transaction as SmsOtpSummaryTransaction,
+ Announcement as SmsOtpSummaryAnnouncement
+} from './screens/Sms/OtpManagement/Section';
+
+import {
+ BulkMessage as SmsAnnouncementBulk,
+ Summary as SmsAnnouncementSummary,
+ Transaction as SmsAnnouncementTransaction
+} from './screens/Sms/Announcements';
+
+import {
+ Transaction as SmsAnnouncementSummaryTransaction,
+ Announcement as SmsAnnouncementSummaryAnnouncement
+} from './screens/Sms/Announcements/Section';
+
+import {
+ Blocked as BlockedSms,
+ BlockNumber
+} from './screens/Sms/BlockedNumbers';
+
+import {
+ Anomaly as AnomalySms
+} from './screens/Sms/AnomalyReport';
+
+import {
+ Verify as VerifyWa
+} from './screens/Wa/Verify';
+
+import {
+ Auth as AuthWa,
+ Message as MessageWa
+} from './screens/Wa/Verify/Section';
+
+import {
+ Registration as RegistrationWa,
+ Profile as ProfileWa,
+ Template as TemplateWa,
+ Integration as IntegrationWa
+} from './screens/Wa/Manage';
+import { CreateSettings } from './screens/Wa/Manage/Content';
const App = () => {
return (
@@ -89,12 +137,6 @@ const App = () => {
{/* Default route */}
} />
- {/* Add routes for the verify section */}
- {/* } />
- } />
- } />
- } />
- } /> */}
{/* Biometric - Face Recognition (Summary) */}
} />
@@ -130,6 +172,54 @@ const App = () => {
} />
+ {/* Sms Services - OTP (Settings) */}
+ } />
+ {/* Sms Services - OTP (Summary) */}
+ }>
+ {/* Anak rute */}
+ } />
+ } />
+ {/* Default route */}
+ } />
+
+ {/* Sms Services - OTP (Transaction) */}
+ } />
+ {/* Sms Services - OTP (Detail) */}
+ } />
+
+ {/* Sms Services - Announcements */}
+ } />
+ }>
+ {/* Anak rute */}
+ } />
+ } />
+ {/* Default route */}
+ } />
+
+ } />
+
+ {/* Sms Services - Block Numbers */}
+ }>
+ } />
+
+ {/* Sms Services - Anomaly Report */}
+ }>
+
+ {/* WhatsApp - Verify */}
+ }>
+ {/* Anak rute */}
+ } />
+ } />
+ {/* Default route */}
+ } />
+
+
+ {/* WhatsApp - Manage */}
+ } />
+ } />
+ } />
+ } />
+ } />
{/* } /> */}
{/* Continue for each link */}
diff --git a/src/assets/css/app.css b/src/assets/css/app.css
index 92c6aa8..ac6a1f5 100644
--- a/src/assets/css/app.css
+++ b/src/assets/css/app.css
@@ -11287,3 +11287,4 @@ body {
background-color: red;
font-size: 10px;
}
+
diff --git a/src/assets/images/WAB-1.png b/src/assets/images/WAB-1.png
new file mode 100644
index 0000000..df00406
Binary files /dev/null and b/src/assets/images/WAB-1.png differ
diff --git a/src/assets/images/WAB-2.png b/src/assets/images/WAB-2.png
new file mode 100644
index 0000000..f6cd326
Binary files /dev/null and b/src/assets/images/WAB-2.png differ
diff --git a/src/assets/images/index.js b/src/assets/images/index.js
index c6ccc7c..759d167 100644
--- a/src/assets/images/index.js
+++ b/src/assets/images/index.js
@@ -3,11 +3,15 @@ import Logo from './Logo.png';
import DashboardImg from './dashboard-img.png';
import DummyKtp from './Dummy-Ktp.png';
import ServerDownAnimation from './server-down.gif';
+import WAB_1 from './WAB-1.png';
+import WAB_2 from './WAB-2.png';
export {
ProfileImage,
Logo,
DashboardImg,
DummyKtp,
- ServerDownAnimation
+ ServerDownAnimation,
+ WAB_1,
+ WAB_2
}
\ No newline at end of file
diff --git a/src/components/Sidebar/dataMenu.js b/src/components/Sidebar/dataMenu.js
index 10f7077..238990c 100644
--- a/src/components/Sidebar/dataMenu.js
+++ b/src/components/Sidebar/dataMenu.js
@@ -137,10 +137,10 @@ const dataMenu = [
name: 'WhatsApp Management', // Changed the name
target: 'collapseWaManage',
subMenus: [
- { name: 'Register Business Account', link: '/wa-manage-register'}, // Changed the name
- { name: 'WhatsApp Profile Settings', link: '/wa-manage-profile'}, // Changed the name
- { name: 'Message Templates', link: '/wa-manage-template'}, // Changed the name
- { name: 'Integration Settings', link: '/wa-manage-integration'}, // Changed the name
+ { name: 'Register Business Account', link: '/wa-registration'}, // Changed the name
+ { name: 'WhatsApp Profile Settings', link: '/wa-profile'}, // Changed the name
+ { name: 'Message Templates', link: '/wa-template'}, // Changed the name
+ { name: 'Integration Settings', link: '/wa-integration'}, // Changed the name
],
},
{
diff --git a/src/screens/Biometric/FaceRecognition/Section/Verify.jsx b/src/screens/Biometric/FaceRecognition/Section/Verify.jsx
index 81fff5d..af005dd 100644
--- a/src/screens/Biometric/FaceRecognition/Section/Verify.jsx
+++ b/src/screens/Biometric/FaceRecognition/Section/Verify.jsx
@@ -667,7 +667,7 @@ const Verify = () => {
menuPlacement="auto"
inputValue={inputValueApplication}
onInputChange={handleInputChangeApplication} // Limit input length for Application ID
- />
+ />
{applicationError && {applicationError}}
diff --git a/src/screens/Biometric/FaceRecognition/Section/index.js b/src/screens/Biometric/FaceRecognition/Section/index.js
index 5192539..f4ea845 100644
--- a/src/screens/Biometric/FaceRecognition/Section/index.js
+++ b/src/screens/Biometric/FaceRecognition/Section/index.js
@@ -1,12 +1,12 @@
import Enroll from "./Enroll";
-import VerifySection from "./Verify";
+import Verify from "./Verify";
import Compare from "./Compare"
import Liveness from "./Liveness"
import Search from "./Search"
export {
Enroll,
- VerifySection,
+ Verify,
Compare,
Liveness,
Search
diff --git a/src/screens/Biometric/FaceRecognition/Verify.jsx b/src/screens/Biometric/FaceRecognition/Verify.jsx
index 160db3e..6fb6f34 100644
--- a/src/screens/Biometric/FaceRecognition/Verify.jsx
+++ b/src/screens/Biometric/FaceRecognition/Verify.jsx
@@ -5,7 +5,7 @@ import {
Compare,
Liveness,
Search,
- VerifySection
+ Verify as VerifySection
} from './Section';
const Verify = () => {
diff --git a/src/screens/Biometric/FaceRecognition/index.js b/src/screens/Biometric/FaceRecognition/index.js
index ad6ea8f..e77be84 100644
--- a/src/screens/Biometric/FaceRecognition/index.js
+++ b/src/screens/Biometric/FaceRecognition/index.js
@@ -1,9 +1,9 @@
-import FaceVerify from "./Verify";
-import FaceSummary from "./Summary";
-import FaceTransaction from "./Transaction";
+import Verify from "./Verify";
+import Summary from "./Summary";
+import Transaction from "./Transaction";
export {
- FaceVerify,
- FaceSummary,
- FaceTransaction
+ Verify,
+ Summary,
+ Transaction
}
\ No newline at end of file
diff --git a/src/screens/Biometric/OcrDocument/index.js b/src/screens/Biometric/OcrDocument/index.js
index 55ba4d1..e77be84 100644
--- a/src/screens/Biometric/OcrDocument/index.js
+++ b/src/screens/Biometric/OcrDocument/index.js
@@ -1,9 +1,9 @@
-import VerifyDoc from "./Verify";
-import SummaryDoc from "./Summary";
-import TransactionDoc from "./Transaction";
+import Verify from "./Verify";
+import Summary from "./Summary";
+import Transaction from "./Transaction";
export {
- VerifyDoc,
- SummaryDoc,
- TransactionDoc
+ Verify,
+ Summary,
+ Transaction
}
\ No newline at end of file
diff --git a/src/screens/Biometric/OcrKtp/Verify.jsx b/src/screens/Biometric/OcrKtp/Verify.jsx
index cf2d152..90850eb 100644
--- a/src/screens/Biometric/OcrKtp/Verify.jsx
+++ b/src/screens/Biometric/OcrKtp/Verify.jsx
@@ -353,7 +353,8 @@ const Verify = () => {
color: '#721c24',
fontSize: '14px',
margin: '0',
- },loadingOverlay: {
+ },
+ loadingOverlay: {
position: 'fixed', // Gunakan fixed untuk overlay penuh layar
top: 0,
left: 0,
diff --git a/src/screens/Biometric/OcrKtp/index.js b/src/screens/Biometric/OcrKtp/index.js
index a48ac53..feadb1f 100644
--- a/src/screens/Biometric/OcrKtp/index.js
+++ b/src/screens/Biometric/OcrKtp/index.js
@@ -1,11 +1,11 @@
-import VerifyKtp from "./Verify";
-import ManageKtp from "./Manage";
-import SummaryKtp from "./Summary";
-import TransactionKtp from "./Transaction";
+import Verify from "./Verify";
+import Manage from "./Manage";
+import Summary from "./Summary";
+import Transaction from "./Transaction";
export {
- VerifyKtp,
- ManageKtp,
- SummaryKtp,
- TransactionKtp
+ Verify,
+ Manage,
+ Summary,
+ Transaction
}
\ No newline at end of file
diff --git a/src/screens/Biometric/OcrNpwp/index.js b/src/screens/Biometric/OcrNpwp/index.js
index 28800e9..e77be84 100644
--- a/src/screens/Biometric/OcrNpwp/index.js
+++ b/src/screens/Biometric/OcrNpwp/index.js
@@ -1,9 +1,9 @@
-import VerifyNpwp from "./Verify";
-import SummaryNpwp from "./Summary";
-import TransactionNpwp from "./Transaction";
+import Verify from "./Verify";
+import Summary from "./Summary";
+import Transaction from "./Transaction";
export {
- VerifyNpwp,
- SummaryNpwp,
- TransactionNpwp
+ Verify,
+ Summary,
+ Transaction
}
\ No newline at end of file
diff --git a/src/screens/Biometric/OcrSim/index.js b/src/screens/Biometric/OcrSim/index.js
index 2f272a6..e77be84 100644
--- a/src/screens/Biometric/OcrSim/index.js
+++ b/src/screens/Biometric/OcrSim/index.js
@@ -1,9 +1,9 @@
-import VerifySim from "./Verify";
-import SummarySim from "./Summary";
-import TransactionSim from "./Transaction";
+import Verify from "./Verify";
+import Summary from "./Summary";
+import Transaction from "./Transaction";
export {
- VerifySim,
- SummarySim,
- TransactionSim
+ Verify,
+ Summary,
+ Transaction
}
\ No newline at end of file
diff --git a/src/screens/Sms/Announcements/Bulk.jsx b/src/screens/Sms/Announcements/Bulk.jsx
new file mode 100644
index 0000000..b51beae
--- /dev/null
+++ b/src/screens/Sms/Announcements/Bulk.jsx
@@ -0,0 +1,295 @@
+import React, { useState, useEffect } from 'react'
+import { CloudDownload } from '../../../assets/icon';
+
+const Bulk = () => {
+ const BASE_URL = process.env.REACT_APP_BASE_URL
+ const API_KEY = process.env.REACT_APP_API_KEY
+
+ const [selectedImageName, setSelectedImageName] = useState('');
+ const [errorMessage, setErrorMessage] = useState('');
+ const [applicationId, setApplicationId] = useState('');
+ const [applicationIds, setApplicationIds] = useState([]);
+ const [isSelectOpen, setIsSelectOpen] = useState(false);
+ const [isMobile, setIsMobile] = useState(false);
+
+ useEffect(() => {
+ const handleResize = () => {
+ setIsMobile(window.innerWidth <= 768); // Set mobile state based on window width
+ };
+
+ handleResize(); // Initial check on component mount
+ window.addEventListener('resize', handleResize); // Listen for resize events
+
+ return () => {
+ window.removeEventListener('resize', handleResize); // Cleanup on component unmount
+ };
+ }, []);
+
+ useEffect(() => {
+ const fetchApplicationIds = async () => {
+ try {
+ const url = `${BASE_URL}/application/list`;
+ console.log('Fetching URL:', url); // Log the URL
+
+ const response = await fetch(url, {
+ method: 'GET',
+ headers: {
+ 'accept': 'application/json',
+ 'x-api-key': `${API_KEY}`,
+ },
+ });
+
+ const data = await response.json();
+
+ if (data.status_code === 200) {
+ const ids = data.details.data.map(app => app.id);
+ console.log('Application Id: ' + ids); // Log the IDs
+ setApplicationIds(data.details.data); // Update state with the fetched data
+ } else {
+ console.error('Failed to fetch data:', data.details.message);
+ }
+ } catch (error) {
+ console.error('Error fetching application IDs:', error);
+ }
+ };
+
+ fetchApplicationIds();
+ }, []);
+
+ const handleFocus = () => {
+ setIsSelectOpen(true);
+ };
+
+ const handleBlur = () => {
+ setIsSelectOpen(false);
+ };
+
+ const handleFileUpload = (event) => {
+ const file = event.target.files[0];
+
+ if (file && (file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/jpg')) {
+ setSelectedImageName(file.name);
+ setErrorMessage('');
+ } else {
+ alert('Please upload a valid image file (JPG, JPEG, PNG).');
+ }
+ };
+
+ return (
+
+
+
+
+
+
Check Our Service Status
+
+ Access pre-approved message templates designed for various purposes, including customer support, appointment reminders, order updates, and more.
+ Simplify your communication by using these ready-to-go formats.
+
+ Get started now by creating an Application ID and explore all the demo services available on the dashboard.
+ Experience the ease and flexibility of trying out all our features firsthand.
+
+ );
+};
+
+const styles = {
+ container: {
+ margin: '3rem',
+ },
+ filters: {
+ display: 'flex',
+ justifyContent: 'space-between',
+ marginBottom: '20px',
+ flexDirection: 'row', // Horizontal layout on desktop
+ gap: '10px',
+ },
+ filtersMobile: {
+ display: 'flex',
+ flexDirection: 'column', // Vertical layout on mobile
+ gap: '15px',
+ },
+ filterRow: {
+ display: 'flex',
+ justifyContent: 'space-between',
+ gap: '10px',
+ width: '100%',
+ },
+ filterInput: {
+ padding: '10px',
+ border: '1px solid #ccc',
+ borderRadius: '5px',
+ fontSize: '16px',
+ width: '48%', // Adjust width to take up 48% of the row for input elements
+ },
+ applyButton: {
+ backgroundColor: '#0542cc',
+ color: '#fff',
+ padding: '10px 20px',
+ border: 'none',
+ borderRadius: '5px',
+ cursor: 'pointer',
+ width: '48%', // Adjust button width to take half of the row
+ },
+ cancelButton: {
+ backgroundColor: '#fff',
+ color: '#000',
+ padding: '10px 20px',
+ border: '1px solid gray',
+ borderRadius: '5px',
+ cursor: 'pointer',
+ width: '48%', // Adjust button width to take half of the row
+ },
+ summaryContainer: {
+ padding: '20px',
+ border: '0.1px solid rgba(0, 0, 0, 0.2)',
+ borderLeft: '4px solid #0542cc',
+ borderRadius: '10px',
+ width: '100%',
+ },
+ summaryCards: {
+ display: 'grid',
+ gridTemplateColumns: 'repeat(4, 1fr)', // 4 columns on desktop
+ gap: '20px',
+ marginTop: '20px',
+ },
+ summaryCardsMobile: {
+ display: 'grid',
+ gridTemplateColumns: '1fr', // 1 column on mobile (full width)
+ gap: '15px',
+ marginTop: '20px',
+ },
+ card: {
+ display: 'flex',
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'space-between',
+ padding: '15px', // Reduced padding for a more compact card
+ backgroundColor: 'white',
+ borderRadius: '8px', // Slightly smaller border-radius for a sharper look
+ boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)',
+ textAlign: 'left',
+ minWidth: '200px', // Ensure minimum width for mobile responsiveness
+ maxWidth: '300px', // Limit the maximum width to prevent cards from becoming too wide
+ width: '100%',
+ transition: 'transform 0.3s ease', // Smooth transition for hover effects
+ },
+ cardHover: {
+ transform: 'scale(1.05)', // Slight zoom on hover for interactivity
+ },
+ iconContainer: {
+ display: 'flex',
+ justifyContent: 'flex-end',
+ alignItems: 'center',
+ },
+ textContainer: {
+ display: 'flex',
+ flexDirection: 'column',
+ },
+ icon: {
+ width: '36px', // Smaller icon size for a more compact card
+ height: '36px', // Matching height for the icon
+ },
+ cardTitle: {
+ fontSize: '18px', // Smaller font size for title to prevent overflow
+ margin: '0',
+ fontWeight: 'bold',
+ },
+ cardText: {
+ fontSize: '12px', // Smaller text size for description to keep it balanced
+ color: '#555',
+ },
+
+ // Optional: Hover effect for the cards to enhance interactivity
+ cardHover: {
+ transform: 'scale(1.05)', // Slight zoom effect when hovering over card
+ },
+
+ // Media query for smaller screens (e.g., phones in portrait mode)
+ "@media (max-width: 768px)": {
+ summaryCards: {
+ gridTemplateColumns: '1fr', // 1 column on mobile screens
+ },
+ card: {
+ padding: '10px', // Smaller padding for smaller screens
+ minWidth: '180px', // Adjust min width for mobile devices
+ maxWidth: '250px', // Adjust max width for mobile devices
+ },
+ icon: {
+ width: '30px', // Slightly smaller icon size for mobile
+ height: '30px', // Matching height for mobile icon
+ },
+ cardTitle: {
+ fontSize: '16px', // Smaller font size for the card title
+ },
+ cardText: {
+ fontSize: '10px', // Smaller font size for card description
+ },
+ },
+};
+
+export default Announcement;
diff --git a/src/screens/Sms/Announcements/Section/Transaction.jsx b/src/screens/Sms/Announcements/Section/Transaction.jsx
new file mode 100644
index 0000000..6ea2e67
--- /dev/null
+++ b/src/screens/Sms/Announcements/Section/Transaction.jsx
@@ -0,0 +1,271 @@
+import React, { useState, useEffect } from 'react';
+import {
+ Extract,
+ Quality,
+ AsyncIcon,
+ QualityAsync,
+ Transaction as TransactionIcon,
+} from '../../../../assets/icon';
+
+const Transaction = () => {
+ const [startDate, setStartDate] = useState('');
+ const [endDate, setEndDate] = useState('');
+ const [application, setApplication] = useState('');
+ const [isMobile, setIsMobile] = useState(false);
+
+ const menuData = [
+ { id: 1, value: '150', label: 'Total Transaction', icon: TransactionIcon },
+ { id: 2, value: '4', label: 'Total Extract', icon: Extract },
+ { id: 3, value: '65', label: 'Total Quality', icon: Quality },
+ { id: 6, value: '900', label: 'Total Extract Async', icon: AsyncIcon },
+ { id: 4, value: '22', label: 'Total Quality Async', icon: QualityAsync },
+ ];
+
+ const handleApply = () => {
+ console.log({ startDate, endDate, application });
+ };
+
+ const handleCancel = () => {
+ setStartDate('');
+ setEndDate('');
+ setApplication('');
+ };
+
+ // Detect if the device is mobile based on window width
+ useEffect(() => {
+ const handleResize = () => {
+ setIsMobile(window.innerWidth <= 768); // Define mobile screen as width <= 768px
+ };
+
+ // Initialize on component mount
+ handleResize();
+
+ // Add event listener to update state on resize
+ window.addEventListener('resize', handleResize);
+
+ // Cleanup event listener on component unmount
+ return () => {
+ window.removeEventListener('resize', handleResize);
+ };
+ }, []);
+
+ return (
+
+ {/* Welcome Message */}
+
+
+
+
+ Alert
+
+
+ Get started now by creating an Application ID and explore all the demo services available on the dashboard.
+ Experience the ease and flexibility of trying out all our features firsthand.
+
+ );
+};
+
+const styles = {
+ container: {
+ margin: '3rem',
+ },
+ filters: {
+ display: 'flex',
+ justifyContent: 'space-between',
+ marginBottom: '20px',
+ flexDirection: 'row', // Horizontal layout on desktop
+ gap: '10px',
+ },
+ filtersMobile: {
+ display: 'flex',
+ flexDirection: 'column', // Vertical layout on mobile
+ gap: '15px',
+ },
+ filterRow: {
+ display: 'flex',
+ justifyContent: 'space-between',
+ gap: '10px',
+ width: '100%',
+ },
+ filterInput: {
+ padding: '10px',
+ border: '1px solid #ccc',
+ borderRadius: '5px',
+ fontSize: '16px',
+ width: '48%', // Adjust width to take up 48% of the row for input elements
+ },
+ applyButton: {
+ backgroundColor: '#0542cc',
+ color: '#fff',
+ padding: '10px 20px',
+ border: 'none',
+ borderRadius: '5px',
+ cursor: 'pointer',
+ width: '48%', // Adjust button width to take half of the row
+ },
+ cancelButton: {
+ backgroundColor: '#fff',
+ color: '#000',
+ padding: '10px 20px',
+ border: '1px solid gray',
+ borderRadius: '5px',
+ cursor: 'pointer',
+ width: '48%', // Adjust button width to take half of the row
+ },
+ summaryContainer: {
+ padding: '20px',
+ border: '0.1px solid rgba(0, 0, 0, 0.2)',
+ borderLeft: '4px solid #0542cc',
+ borderRadius: '10px',
+ width: '100%',
+ },
+ summaryCards: {
+ display: 'grid',
+ gridTemplateColumns: 'repeat(4, 1fr)', // 4 columns on desktop
+ gap: '20px',
+ marginTop: '20px',
+ },
+ summaryCardsMobile: {
+ display: 'grid',
+ gridTemplateColumns: '1fr', // 1 column on mobile (full width)
+ gap: '15px',
+ marginTop: '20px',
+ },
+ card: {
+ display: 'flex',
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'space-between',
+ padding: '15px', // Reduced padding for a more compact card
+ backgroundColor: 'white',
+ borderRadius: '8px', // Slightly smaller border-radius for a sharper look
+ boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)',
+ textAlign: 'left',
+ minWidth: '200px', // Ensure minimum width for mobile responsiveness
+ maxWidth: '300px', // Limit the maximum width to prevent cards from becoming too wide
+ width: '100%',
+ transition: 'transform 0.3s ease', // Smooth transition for hover effects
+ },
+ cardHover: {
+ transform: 'scale(1.05)', // Slight zoom on hover for interactivity
+ },
+ iconContainer: {
+ display: 'flex',
+ justifyContent: 'flex-end',
+ alignItems: 'center',
+ },
+ textContainer: {
+ display: 'flex',
+ flexDirection: 'column',
+ },
+ icon: {
+ width: '36px', // Smaller icon size for a more compact card
+ height: '36px', // Matching height for the icon
+ },
+ cardTitle: {
+ fontSize: '18px', // Smaller font size for title to prevent overflow
+ margin: '0',
+ fontWeight: 'bold',
+ },
+ cardText: {
+ fontSize: '12px', // Smaller text size for description to keep it balanced
+ color: '#555',
+ },
+
+ // Optional: Hover effect for the cards to enhance interactivity
+ cardHover: {
+ transform: 'scale(1.05)', // Slight zoom effect when hovering over card
+ },
+
+ // Media query for smaller screens (e.g., phones in portrait mode)
+ "@media (max-width: 768px)": {
+ summaryCards: {
+ gridTemplateColumns: '1fr', // 1 column on mobile screens
+ },
+ card: {
+ padding: '10px', // Smaller padding for smaller screens
+ minWidth: '180px', // Adjust min width for mobile devices
+ maxWidth: '250px', // Adjust max width for mobile devices
+ },
+ icon: {
+ width: '30px', // Slightly smaller icon size for mobile
+ height: '30px', // Matching height for mobile icon
+ },
+ cardTitle: {
+ fontSize: '16px', // Smaller font size for the card title
+ },
+ cardText: {
+ fontSize: '10px', // Smaller font size for card description
+ },
+ },
+};
+
+export default Transaction;
diff --git a/src/screens/Sms/Announcements/Section/index.js b/src/screens/Sms/Announcements/Section/index.js
new file mode 100644
index 0000000..0245fef
--- /dev/null
+++ b/src/screens/Sms/Announcements/Section/index.js
@@ -0,0 +1,7 @@
+import Transaction from "./Transaction";
+import Announcement from "./Announcement";
+
+export {
+ Transaction,
+ Announcement
+}
\ No newline at end of file
diff --git a/src/screens/Sms/Announcements/Summary.jsx b/src/screens/Sms/Announcements/Summary.jsx
new file mode 100644
index 0000000..e7ec3f7
--- /dev/null
+++ b/src/screens/Sms/Announcements/Summary.jsx
@@ -0,0 +1,114 @@
+import React, { useEffect, useState } from 'react';
+import { Link, Routes, Route, useNavigate } from 'react-router-dom';
+import {
+ Transaction as SmsAnnouncementSummaryTransaction,
+ Announcement as SmsAnnouncementSummaryAnnouncement
+} from './Section';
+
+const Summary = () => {
+ const verifyTabs = [
+ { name: 'Transaction', link: 'sms-announcement-transaction' },
+ { name: 'Announcement', link: 'sms-announcement-announcement' },
+ ];
+
+ const [isMobile, setIsMobile] = useState(false);
+ const navigate = useNavigate();
+
+ // Redirect otomatis ke rute default saat akses ke /sms-announcement-transaction
+ useEffect(() => {
+ if (window.location.pathname === '/sms-announcement-summary') {
+ navigate('sms-announcement-transaction', { replace: true });
+ }
+ }, [navigate]);
+
+ // Update state isMobile berdasarkan ukuran layar
+ useEffect(() => {
+ const handleResize = () => {
+ setIsMobile(window.innerWidth <= 768);
+ };
+
+ handleResize();
+ window.addEventListener('resize', handleResize);
+
+ return () => window.removeEventListener('resize', handleResize);
+ }, []);
+
+ return (
+
+ {/* Static Content */}
+
+
+
+ Alert
+
+
+ Get started now by creating an Application ID and explore all the demo services available on the dashboard.
+ Experience the ease and flexibility of trying out all our features firsthand.
+
+ );
+};
+
+const Transaction = () => {
+ const [currentPage, setCurrentPage] = useState(1);
+ const [isMobile, setIsMobile] = useState(false); // State to detect mobile view
+ const [transactionData, setTransactionData] = useState([]);
+ const [sortConfig, setSortConfig] = useState({ key: null, direction: 'asc' }); // Sorting state
+ const dataPerPage = 10; // Data per page (10 data per page)
+
+ const buttonData = [
+ { label: 'Copy', enabled: true },
+ { label: 'CSV', enabled: true },
+ { label: 'Excel', enabled: true },
+ { label: 'PDF', enabled: true },
+ { label: 'Print', enabled: true },
+ { label: 'Column Visibility', enabled: true },
+ ];
+
+
+ // Generate 691 dummy transactions
+ const generateDummyData = (numOfItems) => {
+ const transactionData = [];
+
+ for (let i = 1; i <= numOfItems; i++) {
+ transactionData.push({
+ transactionId: `TX${String(i).padStart(3, '0')}`, // Transaction ID
+ applicationName: `App ${Math.floor(Math.random() * 5) + 1}`, // Application Name
+ phoneNumber: `+62${Math.floor(Math.random() * 900000000) + 800000000}`, // Random phone number
+ operator: ['Telkomsel', 'Indosat', 'XL', 'Tri', 'Smartfren'][Math.floor(Math.random() * 5)], // Random operator
+ senderId: `SENDER${String(i).padStart(3, '0')}`, // Sender ID
+ segment: ['Retail', 'Finance', 'Health', 'Education', 'Technology'][Math.floor(Math.random() * 5)], // Random segment
+ dateSent: new Date(2023, Math.floor(Math.random() * 12), Math.floor(Math.random() * 28) + 1).toLocaleDateString(), // Random date
+ status: ['Completed', 'Pending', 'Failed'][Math.floor(Math.random() * 3)], // Random status
+ mode: Math.random() > 0.5 ? 'Online' : 'Offline', // Random mode
+ });
+ }
+
+
+ return transactionData;
+ };
+
+
+ // Set the generated transaction data
+ useEffect(() => {
+ setTransactionData(generateDummyData(31)); // count data dummy transactions
+ }, []);
+
+ // Sorting function
+ const sortData = (data, config) => {
+ const { key, direction } = config;
+ return [...data].sort((a, b) => {
+ if (a[key] < b[key]) {
+ return direction === 'asc' ? -1 : 1;
+ }
+ if (a[key] > b[key]) {
+ return direction === 'asc' ? 1 : -1;
+ }
+ return 0;
+ });
+ };
+
+ // Handle column header sort click
+ const handleSort = (key) => {
+ let direction = 'asc';
+ if (sortConfig.key === key && sortConfig.direction === 'asc') {
+ direction = 'desc'; // Toggle direction if the same column is clicked
+ }
+ setSortConfig({ key, direction });
+ };
+
+ // Get the paginated data
+ const getPaginatedData = (data, page, perPage) => {
+ const sortedData = sortData(data, sortConfig);
+ const startIndex = (page - 1) * perPage;
+ const endIndex = startIndex + perPage;
+ return sortedData.slice(startIndex, endIndex);
+ };
+
+ // Handle page change
+ const handlePageChange = (page) => {
+ setCurrentPage(page);
+ };
+
+ // Calculate total pages based on the data and data per page
+ const totalPages = Math.ceil(transactionData.length / dataPerPage);
+
+ // Paginated data
+ const paginatedData = getPaginatedData(transactionData, currentPage, dataPerPage);
+
+ // Detect screen size and update isMobile state
+ useEffect(() => {
+ const handleResize = () => {
+ setIsMobile(window.innerWidth <= 768); // Change 768 to your breakpoint
+ };
+
+ handleResize();
+ window.addEventListener('resize', handleResize);
+ return () => window.removeEventListener('resize', handleResize);
+ }, []);
+
+ return (
+
+ {/* Welcome Message */}
+
+
+
+
+ Alert
+
+
+ Get started now by creating an Application ID and explore all the demo services available on the dashboard.
+ Experience the ease and flexibility of trying out all our features firsthand.
+
+ );
+};
+
+const Anomaly = () => {
+ const [currentPage, setCurrentPage] = useState(1);
+ const [phoneNumber, setPhoneNumber] = useState('');
+ const [isMobile, setIsMobile] = useState(false); // State to detect mobile view
+ const [transactionData, setTransactionData] = useState([]);
+ const [sortConfig, setSortConfig] = useState({ key: null, direction: 'asc' }); // Sorting state
+ const dataPerPage = 10; // Data per page (10 data per page)
+ const [applicationId, setApplicationId] = useState('');
+ const [applicationIds, setApplicationIds] = useState([]);
+ const [inputValueApplication, setInputValueApplication] = useState('');
+
+ const [isLoading, setIsLoading] = useState(false);
+ const [isServer, setIsServer] = useState(true);
+ const [errorMessage, setErrorMessage] = useState('');
+
+ 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
+ }));
+
+ // Validation state
+ const [validationErrors, setValidationErrors] = useState({
+ applicationId: '',
+ file: ''
+ });
+
+
+ const handleApplicationChange = (selectedOption) => {
+ const selectedId = selectedOption.value;
+ const selectedApp = applicationIds.find(app => app.id === parseInt(selectedId));
+
+ setApplicationId(selectedOption ? selectedOption.value : '');
+ };
+
+ const handleInputChangeApplication = (inputValue) => {
+ setInputValueApplication(inputValue);
+ };
+
+ const buttonData = [
+ { label: 'Copy', enabled: true },
+ { label: 'CSV', enabled: true },
+ { label: 'Excel', enabled: true },
+ { label: 'PDF', enabled: true },
+ { label: 'Print', enabled: true },
+ { label: 'Column Visibility', enabled: true },
+ ];
+
+ // Generate 691 dummy transactions
+ const generateDummyData = (numOfItems) => {
+ const transactionData = [];
+
+ for (let i = 1; i <= numOfItems; i++) {
+ transactionData.push({
+ date: new Date(2023, Math.floor(Math.random() * 12), Math.floor(Math.random() * 28) + 1).toLocaleDateString(), // Random date
+ applicationId: `APP${String(Math.floor(Math.random() * 1000)).padStart(3, '0')}`, // Random Application ID
+ applicationName: `App ${Math.floor(Math.random() * 5) + 1}`, // Application Name
+ smsType: ['Promotional', 'Transactional'][Math.floor(Math.random() * 2)], // Random SMS Type
+ phoneNumber: `+62${Math.floor(Math.random() * 900000000) + 800000000}`, // Random phone number
+ tryCount: Math.floor(Math.random() * 5) + 1, // Random try count between 1 and 5
+ });
+ }
+
+
+ return transactionData;
+ };
+
+ 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();
+
+ setTransactionData(generateDummyData(31)); // count data dummy transactions
+ }, []);
+
+ // Sorting function
+ const sortData = (data, config) => {
+ const { key, direction } = config;
+ return [...data].sort((a, b) => {
+ if (a[key] < b[key]) {
+ return direction === 'asc' ? -1 : 1;
+ }
+ if (a[key] > b[key]) {
+ return direction === 'asc' ? 1 : -1;
+ }
+ return 0;
+ });
+ };
+
+ // Handle column header sort click
+ const handleSort = (key) => {
+ let direction = 'asc';
+ if (sortConfig.key === key && sortConfig.direction === 'asc') {
+ direction = 'desc'; // Toggle direction if the same column is clicked
+ }
+ setSortConfig({ key, direction });
+ };
+
+ // Get the paginated data
+ const getPaginatedData = (data, page, perPage) => {
+ const sortedData = sortData(data, sortConfig);
+ const startIndex = (page - 1) * perPage;
+ const endIndex = startIndex + perPage;
+ return sortedData.slice(startIndex, endIndex);
+ };
+
+ // Handle page change
+ const handlePageChange = (page) => {
+ setCurrentPage(page);
+ };
+
+ // Calculate total pages based on the data and data per page
+ const totalPages = Math.ceil(transactionData.length / dataPerPage);
+
+ // Paginated data
+ const paginatedData = getPaginatedData(transactionData, currentPage, dataPerPage);
+
+ // Detect screen size and update isMobile state
+ useEffect(() => {
+ const handleResize = () => {
+ setIsMobile(window.innerWidth <= 768); // Change 768 to your breakpoint
+ };
+
+ handleResize();
+ window.addEventListener('resize', handleResize);
+ return () => window.removeEventListener('resize', handleResize);
+ }, []);
+
+ if (!isServer) {
+ return (
+
+
+
Server tidak dapat diakses
+
{errorMessage || 'Silakan periksa koneksi internet Anda atau coba lagi nanti.'}
+
+
+ );
+ }
+
+ return (
+
+ {/* Inject keyframes for the spinner */}
+
+ {isLoading && (
+
+
+
Loading...
+
+ )}
+
+ {/* Welcome Message */}
+
+
+
+
+ Alert
+
+
+ Get started now by creating an Application ID and explore all the demo services available on the dashboard.
+ Experience the ease and flexibility of trying out all our features firsthand.
+
+
+
+
+
+
+ Please input the phone number first to show data
+
+ );
+};
+
+const BlockedSms = () => {
+ const [currentPage, setCurrentPage] = useState(1);
+ const [isMobile, setIsMobile] = useState(false); // State to detect mobile view
+ const [transactionData, setTransactionData] = useState([]);
+ const [sortConfig, setSortConfig] = useState({ key: null, direction: 'asc' }); // Sorting state
+ const dataPerPage = 10; // Data per page (10 data per page)
+
+ const buttonData = [
+ { label: 'Copy', enabled: true },
+ { label: 'CSV', enabled: true },
+ { label: 'Excel', enabled: true },
+ { label: 'PDF', enabled: true },
+ { label: 'Print', enabled: true },
+ { label: 'Column Visibility', enabled: true },
+ ];
+
+
+ // Generate 691 dummy transactions
+ const generateDummyData = (numOfItems) => {
+ const transactionData = [];
+
+ for (let i = 1; i <= numOfItems; i++) {
+ transactionData.push({
+ transactionId: `TX${String(i).padStart(3, '0')}`,
+ applicationName: `App ${Math.floor(Math.random() * 5) + 1}`,
+ phoneNumber: `+62${Math.floor(Math.random() * 900000000) + 800000000}`, // Random phone number
+ description: [
+ "Transaction processed successfully.",
+ "Payment is pending confirmation.",
+ "Failed to process transaction due to network error.",
+ "Sender ID verification in progress.",
+ "Operator approval required for completion.",
+ "Transaction marked for manual review.",
+ "Processed via offline mode.",
+ "Transaction successfully completed online."
+ ][Math.floor(Math.random() * 8)] // Random description
+ });
+ }
+
+ return transactionData;
+ };
+
+
+ // Set the generated transaction data
+ useEffect(() => {
+ setTransactionData(generateDummyData(3113)); // count data dummy transactions
+ }, []);
+
+ // Sorting function
+ const sortData = (data, config) => {
+ const { key, direction } = config;
+ return [...data].sort((a, b) => {
+ if (a[key] < b[key]) {
+ return direction === 'asc' ? -1 : 1;
+ }
+ if (a[key] > b[key]) {
+ return direction === 'asc' ? 1 : -1;
+ }
+ return 0;
+ });
+ };
+
+ // Handle column header sort click
+ const handleSort = (key) => {
+ let direction = 'asc';
+ if (sortConfig.key === key && sortConfig.direction === 'asc') {
+ direction = 'desc'; // Toggle direction if the same column is clicked
+ }
+ setSortConfig({ key, direction });
+ };
+
+ // Get the paginated data
+ const getPaginatedData = (data, page, perPage) => {
+ const sortedData = sortData(data, sortConfig);
+ const startIndex = (page - 1) * perPage;
+ const endIndex = startIndex + perPage;
+ return sortedData.slice(startIndex, endIndex);
+ };
+
+ // Handle page change
+ const handlePageChange = (page) => {
+ setCurrentPage(page);
+ };
+
+ // Calculate total pages based on the data and data per page
+ const totalPages = Math.ceil(transactionData.length / dataPerPage);
+
+ // Paginated data
+ const paginatedData = getPaginatedData(transactionData, currentPage, dataPerPage);
+
+ // Detect screen size and update isMobile state
+ useEffect(() => {
+ const handleResize = () => {
+ setIsMobile(window.innerWidth <= 768); // Change 768 to your breakpoint
+ };
+
+ handleResize();
+ window.addEventListener('resize', handleResize);
+ return () => window.removeEventListener('resize', handleResize);
+ }, []);
+
+ // Define your handle functions
+ const handleEdit = (transactionId) => {
+ console.log(`Edit transaction with ID: ${transactionId}`);
+ // Add your edit logic here
+ };
+
+ const handleShow = (transactionId) => {
+ console.log(`Show transaction with ID: ${transactionId}`);
+ // Add your show logic here
+ };
+
+ const handleDelete = (transactionId) => {
+ console.log(`Delete transaction with ID: ${transactionId}`);
+ // Add your delete logic here
+ };
+
+ return (
+
+ {/* Welcome Message */}
+
+
+
+
+ Alert
+
+
+ Get started now by creating an Application ID and explore all the demo services available on the dashboard.
+ Experience the ease and flexibility of trying out all our features firsthand.
+
+ );
+};
+
+const DetailView = () => {
+ const [currentPage, setCurrentPage] = useState(1);
+ const [phoneNumber, setPhoneNumber] = useState('');
+ const [isMobile, setIsMobile] = useState(false); // State to detect mobile view
+ const [transactionData, setTransactionData] = useState([]);
+ const [sortConfig, setSortConfig] = useState({ key: null, direction: 'asc' }); // Sorting state
+ const dataPerPage = 10; // Data per page (10 data per page)
+
+ const buttonData = [
+ { label: 'Copy', enabled: true },
+ { label: 'CSV', enabled: true },
+ { label: 'Excel', enabled: true },
+ { label: 'PDF', enabled: true },
+ { label: 'Print', enabled: true },
+ { label: 'Column Visibility', enabled: true },
+ ];
+
+
+ // Generate 691 dummy transactions
+ const generateDummyData = (numOfItems) => {
+ const transactionData = [];
+
+ for (let i = 1; i <= numOfItems; i++) {
+ transactionData.push({
+ applicationName: `App ${Math.floor(Math.random() * 5) + 1}`, // Application Name
+ phoneNumber: `+62${Math.floor(Math.random() * 900000000) + 800000000}`, // Random phone number
+ otp: Math.floor(100000 + Math.random() * 900000),
+ dateSent: new Date(2023, Math.floor(Math.random() * 12), Math.floor(Math.random() * 28) + 1).toLocaleDateString(), // Random date
+ status: ['Completed', 'Pending', 'Failed'][Math.floor(Math.random() * 3)], // Random status
+ });
+ }
+
+
+ return transactionData;
+ };
+
+
+ // Set the generated transaction data
+ useEffect(() => {
+ setTransactionData(generateDummyData(31)); // count data dummy transactions
+ }, []);
+
+ // Sorting function
+ const sortData = (data, config) => {
+ const { key, direction } = config;
+ return [...data].sort((a, b) => {
+ if (a[key] < b[key]) {
+ return direction === 'asc' ? -1 : 1;
+ }
+ if (a[key] > b[key]) {
+ return direction === 'asc' ? 1 : -1;
+ }
+ return 0;
+ });
+ };
+
+ // Handle column header sort click
+ const handleSort = (key) => {
+ let direction = 'asc';
+ if (sortConfig.key === key && sortConfig.direction === 'asc') {
+ direction = 'desc'; // Toggle direction if the same column is clicked
+ }
+ setSortConfig({ key, direction });
+ };
+
+ // Get the paginated data
+ const getPaginatedData = (data, page, perPage) => {
+ const sortedData = sortData(data, sortConfig);
+ const startIndex = (page - 1) * perPage;
+ const endIndex = startIndex + perPage;
+ return sortedData.slice(startIndex, endIndex);
+ };
+
+ // Handle page change
+ const handlePageChange = (page) => {
+ setCurrentPage(page);
+ };
+
+ // Calculate total pages based on the data and data per page
+ const totalPages = Math.ceil(transactionData.length / dataPerPage);
+
+ // Paginated data
+ const paginatedData = getPaginatedData(transactionData, currentPage, dataPerPage);
+
+ // Detect screen size and update isMobile state
+ useEffect(() => {
+ const handleResize = () => {
+ setIsMobile(window.innerWidth <= 768); // Change 768 to your breakpoint
+ };
+
+ handleResize();
+ window.addEventListener('resize', handleResize);
+ return () => window.removeEventListener('resize', handleResize);
+ }, []);
+
+ return (
+
+ {/* Welcome Message */}
+
+
+
+
+ Alert
+
+
+ Get started now by creating an Application ID and explore all the demo services available on the dashboard.
+ Experience the ease and flexibility of trying out all our features firsthand.
+
+
+
+
+
+
+ Please input the phone number first to show data
+
+ Get started now by creating an Application ID and explore all the demo services available on the dashboard.
+ Experience the ease and flexibility of trying out all our features firsthand.
+
+ );
+};
+
+const styles = {
+ container: {
+ margin: '3rem',
+ },
+ filters: {
+ display: 'flex',
+ justifyContent: 'space-between',
+ marginBottom: '20px',
+ flexDirection: 'row', // Horizontal layout on desktop
+ gap: '10px',
+ },
+ filtersMobile: {
+ display: 'flex',
+ flexDirection: 'column', // Vertical layout on mobile
+ gap: '15px',
+ },
+ filterRow: {
+ display: 'flex',
+ justifyContent: 'space-between',
+ gap: '10px',
+ width: '100%',
+ },
+ filterInput: {
+ padding: '10px',
+ border: '1px solid #ccc',
+ borderRadius: '5px',
+ fontSize: '16px',
+ width: '48%', // Adjust width to take up 48% of the row for input elements
+ },
+ applyButton: {
+ backgroundColor: '#0542cc',
+ color: '#fff',
+ padding: '10px 20px',
+ border: 'none',
+ borderRadius: '5px',
+ cursor: 'pointer',
+ width: '48%', // Adjust button width to take half of the row
+ },
+ cancelButton: {
+ backgroundColor: '#fff',
+ color: '#000',
+ padding: '10px 20px',
+ border: '1px solid gray',
+ borderRadius: '5px',
+ cursor: 'pointer',
+ width: '48%', // Adjust button width to take half of the row
+ },
+ summaryContainer: {
+ padding: '20px',
+ border: '0.1px solid rgba(0, 0, 0, 0.2)',
+ borderLeft: '4px solid #0542cc',
+ borderRadius: '10px',
+ width: '100%',
+ },
+ summaryCards: {
+ display: 'grid',
+ gridTemplateColumns: 'repeat(4, 1fr)', // 4 columns on desktop
+ gap: '20px',
+ marginTop: '20px',
+ },
+ summaryCardsMobile: {
+ display: 'grid',
+ gridTemplateColumns: '1fr', // 1 column on mobile (full width)
+ gap: '15px',
+ marginTop: '20px',
+ },
+ card: {
+ display: 'flex',
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'space-between',
+ padding: '15px', // Reduced padding for a more compact card
+ backgroundColor: 'white',
+ borderRadius: '8px', // Slightly smaller border-radius for a sharper look
+ boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)',
+ textAlign: 'left',
+ minWidth: '200px', // Ensure minimum width for mobile responsiveness
+ maxWidth: '300px', // Limit the maximum width to prevent cards from becoming too wide
+ width: '100%',
+ transition: 'transform 0.3s ease', // Smooth transition for hover effects
+ },
+ cardHover: {
+ transform: 'scale(1.05)', // Slight zoom on hover for interactivity
+ },
+ iconContainer: {
+ display: 'flex',
+ justifyContent: 'flex-end',
+ alignItems: 'center',
+ },
+ textContainer: {
+ display: 'flex',
+ flexDirection: 'column',
+ },
+ icon: {
+ width: '36px', // Smaller icon size for a more compact card
+ height: '36px', // Matching height for the icon
+ },
+ cardTitle: {
+ fontSize: '18px', // Smaller font size for title to prevent overflow
+ margin: '0',
+ fontWeight: 'bold',
+ },
+ cardText: {
+ fontSize: '12px', // Smaller text size for description to keep it balanced
+ color: '#555',
+ },
+
+ // Optional: Hover effect for the cards to enhance interactivity
+ cardHover: {
+ transform: 'scale(1.05)', // Slight zoom effect when hovering over card
+ },
+
+ // Media query for smaller screens (e.g., phones in portrait mode)
+ "@media (max-width: 768px)": {
+ summaryCards: {
+ gridTemplateColumns: '1fr', // 1 column on mobile screens
+ },
+ card: {
+ padding: '10px', // Smaller padding for smaller screens
+ minWidth: '180px', // Adjust min width for mobile devices
+ maxWidth: '250px', // Adjust max width for mobile devices
+ },
+ icon: {
+ width: '30px', // Slightly smaller icon size for mobile
+ height: '30px', // Matching height for mobile icon
+ },
+ cardTitle: {
+ fontSize: '16px', // Smaller font size for the card title
+ },
+ cardText: {
+ fontSize: '10px', // Smaller font size for card description
+ },
+ },
+};
+
+export default Announcement;
diff --git a/src/screens/Sms/OtpManagement/Section/Transaction.jsx b/src/screens/Sms/OtpManagement/Section/Transaction.jsx
new file mode 100644
index 0000000..6ea2e67
--- /dev/null
+++ b/src/screens/Sms/OtpManagement/Section/Transaction.jsx
@@ -0,0 +1,271 @@
+import React, { useState, useEffect } from 'react';
+import {
+ Extract,
+ Quality,
+ AsyncIcon,
+ QualityAsync,
+ Transaction as TransactionIcon,
+} from '../../../../assets/icon';
+
+const Transaction = () => {
+ const [startDate, setStartDate] = useState('');
+ const [endDate, setEndDate] = useState('');
+ const [application, setApplication] = useState('');
+ const [isMobile, setIsMobile] = useState(false);
+
+ const menuData = [
+ { id: 1, value: '150', label: 'Total Transaction', icon: TransactionIcon },
+ { id: 2, value: '4', label: 'Total Extract', icon: Extract },
+ { id: 3, value: '65', label: 'Total Quality', icon: Quality },
+ { id: 6, value: '900', label: 'Total Extract Async', icon: AsyncIcon },
+ { id: 4, value: '22', label: 'Total Quality Async', icon: QualityAsync },
+ ];
+
+ const handleApply = () => {
+ console.log({ startDate, endDate, application });
+ };
+
+ const handleCancel = () => {
+ setStartDate('');
+ setEndDate('');
+ setApplication('');
+ };
+
+ // Detect if the device is mobile based on window width
+ useEffect(() => {
+ const handleResize = () => {
+ setIsMobile(window.innerWidth <= 768); // Define mobile screen as width <= 768px
+ };
+
+ // Initialize on component mount
+ handleResize();
+
+ // Add event listener to update state on resize
+ window.addEventListener('resize', handleResize);
+
+ // Cleanup event listener on component unmount
+ return () => {
+ window.removeEventListener('resize', handleResize);
+ };
+ }, []);
+
+ return (
+
+ {/* Welcome Message */}
+
+
+
+
+ Alert
+
+
+ Get started now by creating an Application ID and explore all the demo services available on the dashboard.
+ Experience the ease and flexibility of trying out all our features firsthand.
+
+ );
+};
+
+const styles = {
+ container: {
+ margin: '3rem',
+ },
+ filters: {
+ display: 'flex',
+ justifyContent: 'space-between',
+ marginBottom: '20px',
+ flexDirection: 'row', // Horizontal layout on desktop
+ gap: '10px',
+ },
+ filtersMobile: {
+ display: 'flex',
+ flexDirection: 'column', // Vertical layout on mobile
+ gap: '15px',
+ },
+ filterRow: {
+ display: 'flex',
+ justifyContent: 'space-between',
+ gap: '10px',
+ width: '100%',
+ },
+ filterInput: {
+ padding: '10px',
+ border: '1px solid #ccc',
+ borderRadius: '5px',
+ fontSize: '16px',
+ width: '48%', // Adjust width to take up 48% of the row for input elements
+ },
+ applyButton: {
+ backgroundColor: '#0542cc',
+ color: '#fff',
+ padding: '10px 20px',
+ border: 'none',
+ borderRadius: '5px',
+ cursor: 'pointer',
+ width: '48%', // Adjust button width to take half of the row
+ },
+ cancelButton: {
+ backgroundColor: '#fff',
+ color: '#000',
+ padding: '10px 20px',
+ border: '1px solid gray',
+ borderRadius: '5px',
+ cursor: 'pointer',
+ width: '48%', // Adjust button width to take half of the row
+ },
+ summaryContainer: {
+ padding: '20px',
+ border: '0.1px solid rgba(0, 0, 0, 0.2)',
+ borderLeft: '4px solid #0542cc',
+ borderRadius: '10px',
+ width: '100%',
+ },
+ summaryCards: {
+ display: 'grid',
+ gridTemplateColumns: 'repeat(4, 1fr)', // 4 columns on desktop
+ gap: '20px',
+ marginTop: '20px',
+ },
+ summaryCardsMobile: {
+ display: 'grid',
+ gridTemplateColumns: '1fr', // 1 column on mobile (full width)
+ gap: '15px',
+ marginTop: '20px',
+ },
+ card: {
+ display: 'flex',
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'space-between',
+ padding: '15px', // Reduced padding for a more compact card
+ backgroundColor: 'white',
+ borderRadius: '8px', // Slightly smaller border-radius for a sharper look
+ boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)',
+ textAlign: 'left',
+ minWidth: '200px', // Ensure minimum width for mobile responsiveness
+ maxWidth: '300px', // Limit the maximum width to prevent cards from becoming too wide
+ width: '100%',
+ transition: 'transform 0.3s ease', // Smooth transition for hover effects
+ },
+ cardHover: {
+ transform: 'scale(1.05)', // Slight zoom on hover for interactivity
+ },
+ iconContainer: {
+ display: 'flex',
+ justifyContent: 'flex-end',
+ alignItems: 'center',
+ },
+ textContainer: {
+ display: 'flex',
+ flexDirection: 'column',
+ },
+ icon: {
+ width: '36px', // Smaller icon size for a more compact card
+ height: '36px', // Matching height for the icon
+ },
+ cardTitle: {
+ fontSize: '18px', // Smaller font size for title to prevent overflow
+ margin: '0',
+ fontWeight: 'bold',
+ },
+ cardText: {
+ fontSize: '12px', // Smaller text size for description to keep it balanced
+ color: '#555',
+ },
+
+ // Optional: Hover effect for the cards to enhance interactivity
+ cardHover: {
+ transform: 'scale(1.05)', // Slight zoom effect when hovering over card
+ },
+
+ // Media query for smaller screens (e.g., phones in portrait mode)
+ "@media (max-width: 768px)": {
+ summaryCards: {
+ gridTemplateColumns: '1fr', // 1 column on mobile screens
+ },
+ card: {
+ padding: '10px', // Smaller padding for smaller screens
+ minWidth: '180px', // Adjust min width for mobile devices
+ maxWidth: '250px', // Adjust max width for mobile devices
+ },
+ icon: {
+ width: '30px', // Slightly smaller icon size for mobile
+ height: '30px', // Matching height for mobile icon
+ },
+ cardTitle: {
+ fontSize: '16px', // Smaller font size for the card title
+ },
+ cardText: {
+ fontSize: '10px', // Smaller font size for card description
+ },
+ },
+};
+
+export default Transaction;
diff --git a/src/screens/Sms/OtpManagement/Section/index.js b/src/screens/Sms/OtpManagement/Section/index.js
new file mode 100644
index 0000000..0245fef
--- /dev/null
+++ b/src/screens/Sms/OtpManagement/Section/index.js
@@ -0,0 +1,7 @@
+import Transaction from "./Transaction";
+import Announcement from "./Announcement";
+
+export {
+ Transaction,
+ Announcement
+}
\ No newline at end of file
diff --git a/src/screens/Sms/OtpManagement/Settings.jsx b/src/screens/Sms/OtpManagement/Settings.jsx
new file mode 100644
index 0000000..c86c936
--- /dev/null
+++ b/src/screens/Sms/OtpManagement/Settings.jsx
@@ -0,0 +1,146 @@
+import React, { useState } from 'react';
+
+const Settings = () => {
+ const [spamCheckEnabled, setSpamCheckEnabled] = useState(false);
+ const [sequentialPhoneEnabled, setSequentialPhoneEnabled] = useState(false);
+ const [rateLimit, setRateLimit] = useState('');
+
+ return (
+
+
+ {/* Application ID, OTP Length, Expiry Time, and Message Info */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Spam Check & Rate Limit Section */}
+
Spam Check
+
+ When this feature is enabled, any requests that exceed the limit within the specified time period will return error code 429 and a status of 6 (Rejected). These requests will not incur any charges.
+
+ When activated, all incoming request logs will be analyzed using the Verihubs algorithm. If any anomalies are detected, the
+ request will be rejected and will not be forwarded to the operator. The rejected request will receive a status of 6 (Rejected).
+
+ {/* Spam Check Section */}
+
+
+
+ Enable
+
+ {/* Enable Switch */}
+
+ {
+ setSequentialPhoneEnabled(!sequentialPhoneEnabled);
+ console.log(`Phone Number is now ${!sequentialPhoneEnabled ? 'Enabled' : 'Disabled'}`);
+ }}
+ id="spamCheck"
+ />
+
+
+ Get started now by creating an Application ID and explore all the demo services available on the dashboard.
+ Experience the ease and flexibility of trying out all our features firsthand.
+
+ );
+};
+
+const Transaction = () => {
+ const [currentPage, setCurrentPage] = useState(1);
+ const [isMobile, setIsMobile] = useState(false); // State to detect mobile view
+ const [transactionData, setTransactionData] = useState([]);
+ const [sortConfig, setSortConfig] = useState({ key: null, direction: 'asc' }); // Sorting state
+ const dataPerPage = 10; // Data per page (10 data per page)
+
+ const buttonData = [
+ { label: 'Copy', enabled: true },
+ { label: 'CSV', enabled: true },
+ { label: 'Excel', enabled: true },
+ { label: 'PDF', enabled: true },
+ { label: 'Print', enabled: true },
+ { label: 'Column Visibility', enabled: true },
+ ];
+
+
+ // Generate 691 dummy transactions
+ const generateDummyData = (numOfItems) => {
+ const transactionData = [];
+
+ for (let i = 1; i <= numOfItems; i++) {
+ transactionData.push({
+ transactionId: `TX${String(i).padStart(3, '0')}`, // Transaction ID
+ applicationName: `App ${Math.floor(Math.random() * 5) + 1}`, // Application Name
+ phoneNumber: `+62${Math.floor(Math.random() * 900000000) + 800000000}`, // Random phone number
+ operator: ['Telkomsel', 'Indosat', 'XL', 'Tri', 'Smartfren'][Math.floor(Math.random() * 5)], // Random operator
+ senderId: `SENDER${String(i).padStart(3, '0')}`, // Sender ID
+ dateSent: new Date(2023, Math.floor(Math.random() * 12), Math.floor(Math.random() * 28) + 1).toLocaleDateString(), // Random date
+ status: ['Completed', 'Pending', 'Failed'][Math.floor(Math.random() * 3)], // Random status
+ mode: Math.random() > 0.5 ? 'Online' : 'Offline', // Random mode
+ });
+ }
+
+
+ return transactionData;
+ };
+
+
+ // Set the generated transaction data
+ useEffect(() => {
+ setTransactionData(generateDummyData(31)); // count data dummy transactions
+ }, []);
+
+ // Sorting function
+ const sortData = (data, config) => {
+ const { key, direction } = config;
+ return [...data].sort((a, b) => {
+ if (a[key] < b[key]) {
+ return direction === 'asc' ? -1 : 1;
+ }
+ if (a[key] > b[key]) {
+ return direction === 'asc' ? 1 : -1;
+ }
+ return 0;
+ });
+ };
+
+ // Handle column header sort click
+ const handleSort = (key) => {
+ let direction = 'asc';
+ if (sortConfig.key === key && sortConfig.direction === 'asc') {
+ direction = 'desc'; // Toggle direction if the same column is clicked
+ }
+ setSortConfig({ key, direction });
+ };
+
+ // Get the paginated data
+ const getPaginatedData = (data, page, perPage) => {
+ const sortedData = sortData(data, sortConfig);
+ const startIndex = (page - 1) * perPage;
+ const endIndex = startIndex + perPage;
+ return sortedData.slice(startIndex, endIndex);
+ };
+
+ // Handle page change
+ const handlePageChange = (page) => {
+ setCurrentPage(page);
+ };
+
+ // Calculate total pages based on the data and data per page
+ const totalPages = Math.ceil(transactionData.length / dataPerPage);
+
+ // Paginated data
+ const paginatedData = getPaginatedData(transactionData, currentPage, dataPerPage);
+
+ // Detect screen size and update isMobile state
+ useEffect(() => {
+ const handleResize = () => {
+ setIsMobile(window.innerWidth <= 768); // Change 768 to your breakpoint
+ };
+
+ handleResize();
+ window.addEventListener('resize', handleResize);
+ return () => window.removeEventListener('resize', handleResize);
+ }, []);
+
+ return (
+
+ {/* Welcome Message */}
+
+
+
+
+ Alert
+
+
+ Get started now by creating an Application ID and explore all the demo services available on the dashboard.
+ Experience the ease and flexibility of trying out all our features firsthand.
+
+ WhatsApp is one of the most popular communication platforms globally. As a Facebook-owned company,
+ setting up WhatsApp Business API requires a Facebook account and Facebook Business Manager.
+ Start leveraging this powerful tool for seamless communication with your customers.
+
+
+
+ );
+};
+
+
+// Step Component (untuk menampilkan 4 langkah yang ada)
+const StepComponent = ({ isMigration, isMobile }) => {
+ const stepsData = isMigration
+ ? [
+ { number: '01', description: 'A Viable Phone Number for WhatsApp' },
+ { number: '02', description: 'Your Business Legal address and details' },
+ ]
+ : [
+ { number: '01', description: 'A Viable Phone Number for WhatsApp' },
+ { number: '02', description: 'Your Business Legal address and details' },
+ { number: '03', description: 'Select Number you want to connect' },
+ { number: '04', description: 'Verify your Facebook Business' },
+ ];
+
+ return (
+
+ {stepsData.map((step, index) => (
+
+
+ {step.number}
+
+
{step.description}
+
+ ))}
+
+ );
+};
+
+// Info Box Component untuk setiap informasi
+const InfoComponent = ({ title, description, onStartNow, isMobile }) => {
+ return (
+
+
{title}
+
{description}
+
+
+ );
+};
+
+// Need Component (Menggabungkan Step dan Info Pertama)
+const Need = ({ onStartNow, isMobile }) => {
+ return (
+
+ WhatsApp is one of the most popular communication platforms globally. As a Facebook-owned company,
+ setting up WhatsApp Business API requires a Facebook account and Facebook Business Manager.
+ Start leveraging this powerful tool for seamless communication with your customers.
+
Ensure your business is fully verified by completing the required steps on Facebook. This process is essential to activate and manage your WhatsApp Business API effectively.
+ );
+};
+
+const Integration = () => {
+ const [currentPage, setCurrentPage] = useState(1);
+ const [isMobile, setIsMobile] = useState(false); // State to detect mobile view
+ const [transactionData, setTransactionData] = useState([]);
+ const [sortConfig, setSortConfig] = useState({ key: null, direction: 'asc' }); // Sorting state
+ const dataPerPage = 10; // Data per page (10 data per page)
+
+ const buttonData = [
+ { label: 'Copy', enabled: true },
+ { label: 'CSV', enabled: true },
+ { label: 'Excel', enabled: true },
+ { label: 'PDF', enabled: true },
+ { label: 'Print', enabled: true },
+ { label: 'Column Visibility', enabled: true },
+ ];
+
+
+ // Generate 691 dummy transactions
+ const generateDummyData = (numOfItems) => {
+ const transactionData = [];
+
+ for (let i = 1; i <= numOfItems; i++) {
+ transactionData.push({
+ channelName: `Channel ${Math.floor(Math.random() * 5) + 1}`, // Random channel name (e.g., Channel 1, Channel 2, ...)
+ channelId: `CH${String(i).padStart(3, '0')}`, // Random channel ID (e.g., CH001, CH002, ...)
+ phoneNumber: `+1${Math.floor(Math.random() * 1000000000)}`, // Random phone number (e.g., +1234567890)
+ status: ['Active', 'Inactive', 'Suspended'][Math.floor(Math.random() * 3)], // Random status
+ creationDate: new Date(2023, Math.floor(Math.random() * 12), Math.floor(Math.random() * 28) + 1).toLocaleDateString(), // Random date
+ tier: ['Tier 1', 'Tier 2', 'Tier 3', 'Tier 4', 'Tier 5'][Math.floor(Math.random() * 5)], // Random tier
+ officialBusinessAccount: `Business ${Math.floor(Math.random() * 5) + 1}`, // Random business account (e.g., Business 1, Business 2, ...)
+ });
+ }
+
+ return transactionData;
+ };
+
+
+ // Set the generated transaction data
+ useEffect(() => {
+ setTransactionData(generateDummyData(52)); // count data dummy transactions
+ }, []);
+
+ // Sorting function
+ const sortData = (data, config) => {
+ const { key, direction } = config;
+ return [...data].sort((a, b) => {
+ if (a[key] < b[key]) {
+ return direction === 'asc' ? -1 : 1;
+ }
+ if (a[key] > b[key]) {
+ return direction === 'asc' ? 1 : -1;
+ }
+ return 0;
+ });
+ };
+
+ // Handle column header sort click
+ const handleSort = (key) => {
+ let direction = 'asc';
+ if (sortConfig.key === key && sortConfig.direction === 'asc') {
+ direction = 'desc'; // Toggle direction if the same column is clicked
+ }
+ setSortConfig({ key, direction });
+ };
+
+ // Get the paginated data
+ const getPaginatedData = (data, page, perPage) => {
+ const sortedData = sortData(data, sortConfig);
+ const startIndex = (page - 1) * perPage;
+ const endIndex = startIndex + perPage;
+ return sortedData.slice(startIndex, endIndex);
+ };
+
+ // Handle page change
+ const handlePageChange = (page) => {
+ setCurrentPage(page);
+ };
+
+ // Calculate total pages based on the data and data per page
+ const totalPages = Math.ceil(transactionData.length / dataPerPage);
+
+ // Paginated data
+ const paginatedData = getPaginatedData(transactionData, currentPage, dataPerPage);
+
+ // Detect screen size and update isMobile state
+ useEffect(() => {
+ const handleResize = () => {
+ setIsMobile(window.innerWidth <= 768); // Change 768 to your breakpoint
+ };
+
+ handleResize();
+ window.addEventListener('resize', handleResize);
+ return () => window.removeEventListener('resize', handleResize);
+ }, []);
+
+ // Define your handle functions
+ const handleEdit = (transactionId) => {
+ console.log(`Edit transaction with ID: ${transactionId}`);
+ // Add your edit logic here
+ };
+
+ const handleShow = (transactionId) => {
+ console.log(`Show transaction with ID: ${transactionId}`);
+ // Add your show logic here
+ };
+
+ const handleDelete = (transactionId) => {
+ console.log(`Delete transaction with ID: ${transactionId}`);
+ // Add your delete logic here
+ };
+
+ return (
+
+ {/* Welcome Message */}
+
+
+
+
+ Alert
+
+
+ Get started now by creating an Application ID and explore all the demo services available on the dashboard.
+ Experience the ease and flexibility of trying out all our features firsthand.
+
+
+
+
+
+
+ {/* Filter Form */}
+
+
{/* Using Flexbox */}
+
+ {/* Dropdown: Business Account */}
+
+ );
+};
+
+const Profile = () => {
+ const [currentPage, setCurrentPage] = useState(1);
+ const [isMobile, setIsMobile] = useState(false); // State to detect mobile view
+ const [transactionData, setTransactionData] = useState([]);
+ const [sortConfig, setSortConfig] = useState({ key: null, direction: 'asc' }); // Sorting state
+ const dataPerPage = 10; // Data per page (10 data per page)
+
+ const buttonData = [
+ { label: 'Copy', enabled: true },
+ { label: 'CSV', enabled: true },
+ { label: 'Excel', enabled: true },
+ { label: 'PDF', enabled: true },
+ { label: 'Print', enabled: true },
+ { label: 'Column Visibility', enabled: true },
+ ];
+
+
+ // Generate 691 dummy transactions
+ const generateDummyData = (numOfItems) => {
+ const transactionData = [];
+
+ for (let i = 1; i <= numOfItems; i++) {
+ transactionData.push({
+ channelName: `Channel ${Math.floor(Math.random() * 5) + 1}`, // Random channel name (e.g., Channel 1, Channel 2, ...)
+ channelId: `CH${String(i).padStart(3, '0')}`, // Random channel ID (e.g., CH001, CH002, ...)
+ phoneNumber: `+1${Math.floor(Math.random() * 1000000000)}`, // Random phone number (e.g., +1234567890)
+ status: ['Active', 'Inactive', 'Suspended'][Math.floor(Math.random() * 3)], // Random status
+ creationDate: new Date(2023, Math.floor(Math.random() * 12), Math.floor(Math.random() * 28) + 1).toLocaleDateString(), // Random date
+ officialBusinessAccount: `Business ${Math.floor(Math.random() * 5) + 1}`, // Random business account (e.g., Business 1, Business 2, ...)
+ });
+ }
+
+ return transactionData;
+ };
+
+
+ // Set the generated transaction data
+ useEffect(() => {
+ setTransactionData(generateDummyData(52)); // count data dummy transactions
+ }, []);
+
+ // Sorting function
+ const sortData = (data, config) => {
+ const { key, direction } = config;
+ return [...data].sort((a, b) => {
+ if (a[key] < b[key]) {
+ return direction === 'asc' ? -1 : 1;
+ }
+ if (a[key] > b[key]) {
+ return direction === 'asc' ? 1 : -1;
+ }
+ return 0;
+ });
+ };
+
+ // Handle column header sort click
+ const handleSort = (key) => {
+ let direction = 'asc';
+ if (sortConfig.key === key && sortConfig.direction === 'asc') {
+ direction = 'desc'; // Toggle direction if the same column is clicked
+ }
+ setSortConfig({ key, direction });
+ };
+
+ // Get the paginated data
+ const getPaginatedData = (data, page, perPage) => {
+ const sortedData = sortData(data, sortConfig);
+ const startIndex = (page - 1) * perPage;
+ const endIndex = startIndex + perPage;
+ return sortedData.slice(startIndex, endIndex);
+ };
+
+ // Handle page change
+ const handlePageChange = (page) => {
+ setCurrentPage(page);
+ };
+
+ // Calculate total pages based on the data and data per page
+ const totalPages = Math.ceil(transactionData.length / dataPerPage);
+
+ // Paginated data
+ const paginatedData = getPaginatedData(transactionData, currentPage, dataPerPage);
+
+ // Detect screen size and update isMobile state
+ useEffect(() => {
+ const handleResize = () => {
+ setIsMobile(window.innerWidth <= 768); // Change 768 to your breakpoint
+ };
+
+ handleResize();
+ window.addEventListener('resize', handleResize);
+ return () => window.removeEventListener('resize', handleResize);
+ }, []);
+
+ // Define your handle functions
+ const handleEdit = (transactionId) => {
+ console.log(`Edit transaction with ID: ${transactionId}`);
+ // Add your edit logic here
+ };
+
+ const handleShow = (transactionId) => {
+ console.log(`Show transaction with ID: ${transactionId}`);
+ // Add your show logic here
+ };
+
+ const handleDelete = (transactionId) => {
+ console.log(`Delete transaction with ID: ${transactionId}`);
+ // Add your delete logic here
+ };
+
+ return (
+
+ {/* Welcome Message */}
+
+
+
+
+ Alert
+
+
+ Get started now by creating an Application ID and explore all the demo services available on the dashboard.
+ Experience the ease and flexibility of trying out all our features firsthand.
+
+
+
+
+
+
+ {/* Filter Form */}
+
+
{/* Menggunakan Flexbox */}
+ {/* Dropdown: Business Account */}
+
+ )
+}
+
+export default Registration
+
+const styles = {
+ container: {
+ marginTop: '3%',
+ padding: '0 15px',
+ },
+ section: {
+ padding: '20px',
+ border: '0.1px solid rgba(0, 0, 0, 0.2)',
+ borderLeft: '4px solid #0542cc',
+ borderRadius: '10px',
+ width: '100%',
+ },
+}
diff --git a/src/screens/Wa/Manage/Template.jsx b/src/screens/Wa/Manage/Template.jsx
new file mode 100644
index 0000000..71919ee
--- /dev/null
+++ b/src/screens/Wa/Manage/Template.jsx
@@ -0,0 +1,346 @@
+import React, { useState, useEffect } from 'react';
+import { Link } from 'react-router-dom';
+import { CopyToClipboard } from 'react-copy-to-clipboard';
+
+const Template = () => {
+ const BASE_URL = process.env.REACT_APP_BASE_URL;
+ const API_KEY = process.env.REACT_APP_API_KEY;
+
+ const [ownership, setOwnership] = useState([]);
+ const [buttonStates, setButtonStates] = useState([]);
+ const [copiedIndex, setCopiedIndex] = useState(null);
+ const [searchTerm, setSearchTerm] = useState(''); // state for search term
+ const [debouncedSearchTerm, setDebouncedSearchTerm] = useState(''); // state for debounced search term
+ const [errorMessage, setErrorMessage] = useState(null); // state for error message
+
+ // Debounce search term input
+ useEffect(() => {
+ const timer = setTimeout(() => {
+ setDebouncedSearchTerm(searchTerm);
+ }, 500); // Delay of 500ms before search term is sent
+ return () => clearTimeout(timer); // Clear timeout when searchTerm changes
+ }, [searchTerm]);
+
+ // Fetch applications based on the search term
+ useEffect(() => {
+ let isMounted = true;
+
+ const fetchApplications = async () => {
+ if (debouncedSearchTerm) {
+ // Fetch by search term
+ try {
+ const response = await fetch(`${BASE_URL}/application/get-by-name/${debouncedSearchTerm}`, {
+ method: 'GET',
+ headers: {
+ 'accept': 'application/json',
+ 'x-api-key': API_KEY,
+ },
+ });
+ const result = await response.json();
+
+ if (response.ok) {
+ if (result.details && result.details.data) {
+ if (isMounted) {
+ setOwnership([result.details.data]);
+ setButtonStates([{ isHovered: false, isActive: false }]);
+ setErrorMessage(null); // Reset error if data found
+ }
+ } else {
+ if (isMounted) {
+ setOwnership([]);
+ setErrorMessage('Data is not found.');
+ }
+ }
+ } else {
+ if (isMounted) {
+ setOwnership([]);
+ setErrorMessage('Data is not found.');
+ }
+ }
+ } catch (error) {
+ console.error('Error fetching applications:', error);
+ if (isMounted) {
+ setOwnership([]);
+ setErrorMessage('Data is not found.');
+ }
+ }
+ } else {
+ // Fetch all applications when there's no search term
+ const fetchAllApplications = async () => {
+ try {
+ const response = await fetch(`${BASE_URL}/application/list`, {
+ method: 'GET',
+ headers: {
+ 'x-api-key': API_KEY,
+ },
+ });
+ const result = await response.json();
+ if (response.ok && result.status_code === 200) {
+ if (isMounted) {
+ setOwnership(result.details.data);
+ setButtonStates(result.details.data.map(() => ({ isHovered: false, isActive: false })));
+ setErrorMessage(null); // Reset error if data found
+ }
+ } else {
+ if (isMounted) {
+ setOwnership([]);
+ setErrorMessage('Error fetching data. Please try again.');
+ }
+ }
+ } catch (error) {
+ console.error('Error fetching all applications:', error);
+ if (isMounted) {
+ setOwnership([]);
+ setErrorMessage('Error fetching data. Please try again.');
+ }
+ }
+ };
+
+ fetchAllApplications();
+ }
+ };
+
+ fetchApplications();
+
+ return () => {
+ isMounted = false; // Set the flag to false on cleanup
+ };
+
+ }, [debouncedSearchTerm]); // Trigger the effect when `debouncedSearchTerm` changes
+
+ const handleMouseEnter = (index) => {
+ setButtonStates((prevStates) => {
+ const newStates = [...prevStates];
+ newStates[index].isHovered = true;
+ return newStates;
+ });
+ };
+
+ const handleMouseLeave = (index) => {
+ setButtonStates((prevStates) => {
+ const newStates = [...prevStates];
+ newStates[index].isHovered = false;
+ return newStates;
+ });
+ };
+
+ const handleChange = (event) => {
+ setSearchTerm(event.target.value);
+ };
+
+ return (
+
+ {/* Welcome Message */}
+
+
+
+
+ Create WhatsApp Business Templates
+
+
+ Design pre-approved message templates tailored for various business needs, such as customer support, appointment reminders, order updates, and more. Streamline communication and ensure consistency with ready-to-use templates.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Filtering Options */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Search Bar */}
+
+
+
+
+
+ {/* Application Cards */}
+
+ {/* Show message if no data or search result */}
+ {ownership.length === 0 && !errorMessage &&
+ {/* Inject keyframes for the spinner */}
+
+
+ {isLoading && (
+
+
+
+ )}
+ {/* Application ID Selection */}
+
+
+
+
+
+
+ Remaining Quota
+
+
+ {selectedQuota}
+ (times)
+
+
+
+
+ {/* Expiry and OTP */}
+
+
+
+ {
+ const value = e.target.value;
+ // Check if the value is empty or a valid number
+ if (value === '' || /^[0-9]+$/.test(value)) {
+ setExpiryId(value === '' ? '' : parseInt(value, 10)); // Update state as number
+ }
+ }}
+ onFocus={handleFocus}
+ onBlur={handleBlur}
+ placeholder="Expiry Time"
+ />
+ {errors.expiryId && {errors.expiryId}}
+
+
+
+
+
+ {
+ const value = e.target.value;
+ // Allow only digits and enforce length constraints
+ if (/^\d{0,6}$/.test(value)) {
+ setOtpId(value);
+ }
+ }}
+ minLength={6} // Enforce minimum length (for validation)
+ maxLength={6} // Enforce maximum length
+ />
+ {errors.otpId && {errors.otpId}}
+
+ Get started now by creating an Application ID and explore all the demo services available on the dashboard.
+ Experience the ease and flexibility of trying out all our features firsthand.
+