Slicing UI
This commit is contained in:
212
src/screens/Wa/Manage/Content/CreateSettings.jsx
Normal file
212
src/screens/Wa/Manage/Content/CreateSettings.jsx
Normal file
@@ -0,0 +1,212 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
const CreateSettings = () => {
|
||||
const location = useLocation();
|
||||
const [isMobile, setIsMobile] = useState(window.innerWidth <= 768);
|
||||
|
||||
useEffect(() => {
|
||||
const handleResize = () => setIsMobile(window.innerWidth <= 768);
|
||||
window.addEventListener('resize', handleResize);
|
||||
return () => window.removeEventListener('resize', handleResize);
|
||||
}, []);
|
||||
|
||||
const Breadcrumb = ({ path }) => {
|
||||
return (
|
||||
<nav style={styles.breadcrumb}>
|
||||
{path.map((item, index) => (
|
||||
<span key={index}>
|
||||
{index > 0 && ' > '}
|
||||
<a
|
||||
href={item.link}
|
||||
style={{
|
||||
...styles.breadcrumbLink,
|
||||
fontWeight: location.pathname === item.link ? 'bold' : 'normal',
|
||||
}}
|
||||
>
|
||||
{item.name}
|
||||
</a>
|
||||
</span>
|
||||
))}
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
const breadcrumbPath = [
|
||||
{ name: 'Wa Integration', link: '/wa-integration' },
|
||||
{ name: 'Add Setting', link: '/wa-createSettings' },
|
||||
];
|
||||
|
||||
return (
|
||||
<div style={styles.container}>
|
||||
<div style={isMobile ? styles.wrapperMobile : styles.wrapper}>
|
||||
<Breadcrumb path={breadcrumbPath} />
|
||||
<h2 style={styles.header}>Create Settings</h2>
|
||||
|
||||
<div style={isMobile ? styles.formWrapperMobile : styles.formWrapper}>
|
||||
{/* Left Form Section */}
|
||||
<div style={styles.formSection}>
|
||||
<div style={styles.inputGroup}>
|
||||
<label style={styles.label}>Application ID</label>
|
||||
<select style={styles.input}>
|
||||
<option value="">Select Application</option>
|
||||
</select>
|
||||
</div>
|
||||
<div style={styles.inputGroup}>
|
||||
<label style={styles.label}>Inbound Configuration</label>
|
||||
<select style={styles.input}>
|
||||
<option value="">Select Configuration</option>
|
||||
</select>
|
||||
</div>
|
||||
<div style={styles.inputGroup}>
|
||||
<label style={styles.label}>Webhook URL</label>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Enter Webhook URL"
|
||||
style={styles.input}
|
||||
/>
|
||||
</div>
|
||||
<div style={styles.inputGroup}>
|
||||
<label style={styles.label}>Event</label>
|
||||
<div style={styles.checkboxGroup}>
|
||||
<label>
|
||||
<input type="checkbox" />
|
||||
Message Created
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" />
|
||||
Message Updated
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<button style={styles.button}>Add Now</button>
|
||||
</div>
|
||||
|
||||
{/* Right WhatsApp Details Section */}
|
||||
<div style={styles.detailSection}>
|
||||
<h3 style={styles.detailHeader}>WhatsApp Channel Detail</h3>
|
||||
<div style={styles.detailItem}>
|
||||
<strong>Business Account Name:</strong> RekanVeri
|
||||
</div>
|
||||
<div style={styles.detailItem}>
|
||||
<strong>WABA ID:</strong> 112917284928438
|
||||
</div>
|
||||
<div style={styles.detailItem}>
|
||||
<strong>WhatsApp Number:</strong> 6287736766208
|
||||
</div>
|
||||
<div style={styles.detailItem}>
|
||||
<strong>Channel Name:</strong> RekanVeri
|
||||
</div>
|
||||
<div style={styles.detailItem}>
|
||||
<strong>Eligible for Inbound:</strong> YES
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateSettings;
|
||||
|
||||
const styles = {
|
||||
container: {
|
||||
padding: '1rem',
|
||||
width: '100%',
|
||||
margin: '0 auto',
|
||||
boxSizing: 'border-box',
|
||||
},
|
||||
wrapper: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
border: '1px solid #ddd',
|
||||
borderRadius: '8px',
|
||||
padding: '1.5rem',
|
||||
boxShadow: '0 2px 5px rgba(0, 0, 0, 0.1)',
|
||||
backgroundColor: '#fff',
|
||||
maxWidth: '1200px',
|
||||
margin: '0 auto',
|
||||
},
|
||||
wrapperMobile: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
border: '1px solid #ddd',
|
||||
borderRadius: '8px',
|
||||
padding: '1rem',
|
||||
boxShadow: '0 2px 5px rgba(0, 0, 0, 0.1)',
|
||||
backgroundColor: '#fff',
|
||||
},
|
||||
breadcrumb: {
|
||||
marginBottom: '1rem',
|
||||
fontSize: '0.9rem',
|
||||
},
|
||||
breadcrumbLink: {
|
||||
textDecoration: 'none',
|
||||
color: '#0542cc',
|
||||
},
|
||||
header: {
|
||||
marginBottom: '1rem',
|
||||
fontSize: '1.5rem',
|
||||
},
|
||||
formWrapper: {
|
||||
display: 'flex',
|
||||
gap: '2rem',
|
||||
marginTop: '1rem',
|
||||
},
|
||||
formWrapperMobile: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '1rem',
|
||||
marginTop: '1rem',
|
||||
},
|
||||
formSection: {
|
||||
flex: 2,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '1rem',
|
||||
},
|
||||
inputGroup: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '0.5rem',
|
||||
},
|
||||
label: {
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
input: {
|
||||
padding: '0.5rem',
|
||||
border: '1px solid #ddd',
|
||||
borderRadius: '4px',
|
||||
width: '100%',
|
||||
},
|
||||
checkboxGroup: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '0.5rem',
|
||||
},
|
||||
button: {
|
||||
padding: '0.8rem 1.5rem',
|
||||
backgroundColor: '#0542cc',
|
||||
color: '#fff',
|
||||
border: 'none',
|
||||
borderRadius: '4px',
|
||||
cursor: 'pointer',
|
||||
alignSelf: 'flex-start',
|
||||
},
|
||||
detailSection: {
|
||||
flex: 1,
|
||||
backgroundColor: '#f5f9ff',
|
||||
padding: '1rem',
|
||||
border: '1px solid #ddd',
|
||||
borderRadius: '8px',
|
||||
},
|
||||
detailHeader: {
|
||||
fontSize: '1.2rem',
|
||||
marginBottom: '1rem',
|
||||
color: '#0542cc',
|
||||
},
|
||||
detailItem: {
|
||||
marginBottom: '0.5rem',
|
||||
fontSize: '0.9rem',
|
||||
},
|
||||
};
|
||||
256
src/screens/Wa/Manage/Content/RegisterContent.jsx
Normal file
256
src/screens/Wa/Manage/Content/RegisterContent.jsx
Normal file
@@ -0,0 +1,256 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { WAB_1, WAB_2 } from '../../../../assets/images';
|
||||
import { RegisterStep } from '../Content';
|
||||
|
||||
// Header component
|
||||
const HeaderComponent = ({ isMobile }) => {
|
||||
return (
|
||||
<div style={isMobile ? styles.headerContainerMobile : styles.headerContainer}>
|
||||
<img src={WAB_1} alt="WAB_1" style={styles.image(isMobile ? 200 : 350, isMobile ? 100 : 170)} />
|
||||
<div style={styles.textContainer}>
|
||||
<h2 style={styles.textTitle}>API For Your WhatsApp Business</h2>
|
||||
<p style={styles.textBody}>
|
||||
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.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
// 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 (
|
||||
<div style={isMobile ? styles.stepsContainerMobile : styles.stepsContainer}>
|
||||
{stepsData.map((step, index) => (
|
||||
<div key={index} style={styles.step}>
|
||||
<div style={styles.stepNumberContainer}>
|
||||
<span style={styles.stepNumber}>{step.number}</span>
|
||||
</div>
|
||||
<p style={styles.stepDescription}>{step.description}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Info Box Component untuk setiap informasi
|
||||
const InfoComponent = ({ title, description, onStartNow, isMobile }) => {
|
||||
return (
|
||||
<div style={isMobile ? styles.infoBoxMobile : styles.infoBox}>
|
||||
<h3>{title}</h3>
|
||||
<p>{description}</p>
|
||||
<button style={styles.startButton} onClick={onStartNow}>Start Now</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Need Component (Menggabungkan Step dan Info Pertama)
|
||||
const Need = ({ onStartNow, isMobile }) => {
|
||||
return (
|
||||
<div style={styles.regisBusinessContainer}>
|
||||
<div style={styles.rowContainer}>
|
||||
<div style={styles.col6}>
|
||||
<StepComponent isMobile={isMobile} />
|
||||
</div>
|
||||
<div style={styles.col6}>
|
||||
<InfoComponent
|
||||
title="What You Need?"
|
||||
description="To register for WhatsApp Business API, you'll need an active phone number,
|
||||
your business's legal address and details, select the number you want to connect, and
|
||||
verify your Facebook Business account to complete the setup."
|
||||
onStartNow={onStartNow}
|
||||
isMobile={isMobile}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Migration Component (Menggabungkan Step dan Info Kedua)
|
||||
const Migration = ({ isMobile }) => {
|
||||
return (
|
||||
<div style={styles.regisBusinessContainer}>
|
||||
<div style={styles.rowContainer}>
|
||||
<div style={styles.col6}>
|
||||
<div style={styles.leftSideContainer}>
|
||||
<img src={WAB_2} alt="WAB_2" style={styles.image(isMobile ? 100 : 180, isMobile ? 100 : 180)} />
|
||||
<StepComponent isMigration={true} isMobile={isMobile} />
|
||||
</div>
|
||||
</div>
|
||||
<div style={styles.col6}>
|
||||
<InfoComponent
|
||||
title="What You Need for Migration?"
|
||||
description="To complete the migration, you'll need a verified Facebook Business account
|
||||
and access to the phone number you wish to migrate."
|
||||
isMobile={isMobile}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
// Main component
|
||||
const RegisterContent = () => {
|
||||
const [isStarted, setIsStarted] = useState(false);
|
||||
const [isMobile, setIsMobile] = useState(window.innerWidth <= 768);
|
||||
|
||||
// Update isMobile state on window resize
|
||||
useEffect(() => {
|
||||
const handleResize = () => setIsMobile(window.innerWidth <= 768);
|
||||
window.addEventListener('resize', handleResize);
|
||||
|
||||
return () => window.removeEventListener('resize', handleResize);
|
||||
}, []);
|
||||
|
||||
const handleStartNow = () => {
|
||||
setIsStarted(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={styles.regisBusinessContainer}>
|
||||
{!isStarted && <HeaderComponent isMobile={isMobile} />}
|
||||
{!isStarted && <Need onStartNow={handleStartNow} isMobile={isMobile} />}
|
||||
{!isStarted && <Migration isMobile={isMobile} />}
|
||||
{isStarted && <RegisterStep />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
const styles = {
|
||||
regisBusinessContainer: {
|
||||
padding: '1rem',
|
||||
margin: 'auto',
|
||||
fontFamily: 'Arial, sans-serif'
|
||||
},
|
||||
|
||||
// Header Content Style
|
||||
headerContainer: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginBottom: '20px',
|
||||
},
|
||||
leftSideContainer: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: '10px',
|
||||
},
|
||||
image: (width, height) => ({
|
||||
width: width,
|
||||
height: height,
|
||||
flex: '1 1 33%',
|
||||
alignSelf: 'flex-start',
|
||||
}),
|
||||
textContainer: {
|
||||
flex: '1 1 66%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-end',
|
||||
paddingLeft: '20px',
|
||||
},
|
||||
textTitle: {
|
||||
fontSize: '24px',
|
||||
fontWeight: 'bold',
|
||||
marginBottom: '10px',
|
||||
},
|
||||
textBody: {
|
||||
fontSize: '16px',
|
||||
lineHeight: '1.5',
|
||||
},
|
||||
rowContainer: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
marginTop: '20px',
|
||||
flexWrap: 'wrap',
|
||||
borderRadius: '8px',
|
||||
border: '1px solid #ddd',
|
||||
boxShadow: '0px 2px 8px rgba(0, 0, 0, 0.1)',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
minHeight: '200px',
|
||||
},
|
||||
col6: {
|
||||
flex: '1 1 50%',
|
||||
padding: '10px',
|
||||
boxSizing: 'border-box',
|
||||
},
|
||||
|
||||
// Step Style
|
||||
stepsContainer: {
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
gap: '10px', // Jarak antar langkah
|
||||
position: 'relative', // Posisi relatif agar connectorLine bisa diposisikan dengan benar
|
||||
},
|
||||
step: {
|
||||
flex: '1 1 16.6%', // Menyusun setiap langkah untuk mengambil 1/6 dari lebar container
|
||||
padding: '10px',
|
||||
boxSizing: 'border-box',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
textAlign: 'center',
|
||||
marginBottom: '10px',
|
||||
position: 'relative', // Agar connectorLine bisa diposisikan di dalam step
|
||||
},
|
||||
stepNumberContainer: {
|
||||
width: '40px', // Menentukan lebar oval
|
||||
height: '40px', // Menentukan tinggi oval
|
||||
borderRadius: '50%', // Membuat bentuk oval
|
||||
backgroundColor: '#0542CC', // Warna latar belakang oval
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginBottom: '8px',
|
||||
},
|
||||
stepNumber: {
|
||||
fontSize: '16px',
|
||||
color: '#fff', // Warna teks putih di dalam oval
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
stepDescription: {
|
||||
fontSize: '14px',
|
||||
lineHeight: '1.5',
|
||||
color: '#333',
|
||||
textAlign: 'center',
|
||||
margin: '10px 0',
|
||||
},
|
||||
|
||||
// Info Style
|
||||
infoBox: {
|
||||
padding: '20px',
|
||||
marginBottom: '20px',
|
||||
},
|
||||
startButton: {
|
||||
backgroundColor: '#0542cc',
|
||||
color: '#fff',
|
||||
padding: '10px 20px',
|
||||
border: 'none',
|
||||
borderRadius: '5px',
|
||||
cursor: 'pointer',
|
||||
marginTop: '15px',
|
||||
},
|
||||
};
|
||||
|
||||
export default RegisterContent;
|
||||
455
src/screens/Wa/Manage/Content/RegisterStep.jsx
Normal file
455
src/screens/Wa/Manage/Content/RegisterStep.jsx
Normal file
@@ -0,0 +1,455 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { WAB_2 } from '../../../../assets/images';
|
||||
|
||||
const HeaderComponent = () => {
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
return (
|
||||
<div style={{ ...styles.headerContainer, flexDirection: isMobile ? 'column' : 'row' }}>
|
||||
<div style={styles.textContainer} className='p-3'>
|
||||
<h2 style={styles.textTitle}>Registration Step</h2>
|
||||
<p style={styles.textBody}>
|
||||
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.
|
||||
</p>
|
||||
</div>
|
||||
<img src={WAB_2} alt="WAB_2" style={styles.image(190, 190)} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const useIsMobile = () => {
|
||||
const [isMobile, setIsMobile] = useState(window.innerWidth <= 768);
|
||||
|
||||
useEffect(() => {
|
||||
const handleResize = () => setIsMobile(window.innerWidth <= 768);
|
||||
window.addEventListener('resize', handleResize);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return isMobile;
|
||||
};
|
||||
|
||||
|
||||
const InfoComponent = ({ title, description, onStartNow }) => {
|
||||
const [isChecked, setIsChecked] = useState(false);
|
||||
const [errorMessage, setErrorMessage] = useState('');
|
||||
|
||||
const handleCheckboxChange = (event) => {
|
||||
setIsChecked(event.target.checked);
|
||||
setErrorMessage('');
|
||||
};
|
||||
|
||||
const handleStartNow = () => {
|
||||
if (!isChecked) {
|
||||
setErrorMessage('Please confirm agreement on above!');
|
||||
} else {
|
||||
onStartNow();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={styles.contentContainer}>
|
||||
<h3>{title}</h3>
|
||||
<p>{description}</p>
|
||||
<ul style={styles.descriptionList}>
|
||||
<li>The number must be owned by your company.</li>
|
||||
<li>Do not use a personal number.</li>
|
||||
<li>It should not be actively registered on WhatsApp Messenger or WhatsApp Business App.</li>
|
||||
<li>You must be able to receive phone calls or SMS on the number during the setup process.</li>
|
||||
</ul>
|
||||
<div style={styles.checkboxContainer}>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="agreement"
|
||||
style={styles.checkbox}
|
||||
checked={isChecked}
|
||||
onChange={handleCheckboxChange}
|
||||
/>
|
||||
<label htmlFor="agreement" style={styles.checkboxLabel}>
|
||||
I have a number with the above requirements
|
||||
</label>
|
||||
</div>
|
||||
{errorMessage && (
|
||||
<p style={{ color: 'red', fontSize: '0.875rem' }}>{errorMessage}</p>
|
||||
)}
|
||||
<button style={styles.startButton} onClick={handleStartNow}>
|
||||
Start Now
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// StepComponent
|
||||
const StepComponent = ({ activeStep }) => {
|
||||
const stepsData = [
|
||||
{ number: '01', description: 'Phone Number to WhatsApp' },
|
||||
{ number: '02', description: 'WhatsApp Business Channel' },
|
||||
{ number: '03', description: 'Verify Your Business on Facebook' },
|
||||
];
|
||||
|
||||
return (
|
||||
<div style={styles.stepsContainer}>
|
||||
{stepsData.map((step, index) => {
|
||||
const isCurrent = activeStep === index;
|
||||
|
||||
return (
|
||||
<div key={index} style={styles.step}>
|
||||
<div
|
||||
style={{
|
||||
...styles.stepNumberContainer,
|
||||
backgroundColor: isCurrent ? '#0542cc' : '#d3d3d3',
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
...styles.stepNumber,
|
||||
color: isCurrent ? '#fff' : '#808080',
|
||||
}}
|
||||
>
|
||||
{step.number}
|
||||
</span>
|
||||
</div>
|
||||
<p
|
||||
style={{
|
||||
...styles.stepDescription,
|
||||
color: isCurrent ? '#000' : 'gray',
|
||||
}}
|
||||
>
|
||||
{step.description}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// FirstContentComponent
|
||||
const FirstContentComponent = ({ onStartNow }) => (
|
||||
<div style={styles.rowContainer}>
|
||||
<div style={styles.col6}>
|
||||
<StepComponent activeStep={0} /> {/* Displays step 1 as active */}
|
||||
</div>
|
||||
<div style={styles.col6}>
|
||||
<InfoComponent
|
||||
title="Following Specifications Step"
|
||||
onStartNow={onStartNow} // Handles "Start Now" button click
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
// SecondContentComponent
|
||||
const SecondContentComponent = ({ onNextStep, onBack }) => {
|
||||
const [isChecked, setIsChecked] = useState(false);
|
||||
const [errorMessage, setErrorMessage] = useState('');
|
||||
|
||||
const handleCheckboxChange = (event) => {
|
||||
setIsChecked(event.target.checked);
|
||||
setErrorMessage(''); // Reset error message if checkbox is changed
|
||||
};
|
||||
|
||||
const handleNextStep = () => {
|
||||
if (!isChecked) {
|
||||
setErrorMessage('Please confirm agreement on above!');
|
||||
} else {
|
||||
onNextStep(); // Proceed to next step if checkbox is checked
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={styles.rowContainer}>
|
||||
<div style={styles.col6}>
|
||||
<StepComponent activeStep={1} /> {/* Displays step 2 as active */}
|
||||
</div>
|
||||
<div style={styles.col6}>
|
||||
<p>You'll need the following information for your WhatsApp Business Channel, Be sure to have them on hand.</p>
|
||||
<ul style={styles.descriptionList}>
|
||||
<li>Your brand's Display Name, shown to customers you chat with.</li>
|
||||
<li>Your company's Legal business name.</li>
|
||||
<li>Your company's official address.</li>
|
||||
</ul>
|
||||
<p>Create your WhatsApp Business Channel</p>
|
||||
<p>You'll be directed to Facebook Business Manager. Complete all the steps in the pop-up in order to create your WhatsApp Business Channel</p>
|
||||
|
||||
<div style={styles.checkboxContainer}>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="agreement"
|
||||
style={styles.checkbox}
|
||||
checked={isChecked}
|
||||
onChange={handleCheckboxChange}
|
||||
/>
|
||||
<label htmlFor="agreement" style={styles.checkboxLabel}>
|
||||
Create Your Own Channel
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{errorMessage && (
|
||||
<p style={{ color: 'red', fontSize: '0.875rem' }}>{errorMessage}</p>
|
||||
)}
|
||||
|
||||
<button onClick={onBack} style={styles.backButton}>
|
||||
Back
|
||||
</button>
|
||||
<button onClick={handleNextStep} style={styles.startButton}>
|
||||
Next Step
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// LastContentComponent (ThirdComponent)
|
||||
const LastContentComponent = ({ onBack }) => {
|
||||
const [isChecked, setIsChecked] = useState(false);
|
||||
const [errorMessage, setErrorMessage] = useState('');
|
||||
|
||||
const handleCheckboxChange = (event) => {
|
||||
setIsChecked(event.target.checked);
|
||||
setErrorMessage(''); // Reset error message if checkbox is changed
|
||||
};
|
||||
|
||||
const handleFinish = () => {
|
||||
if (!isChecked) {
|
||||
setErrorMessage('Please confirm agreement on above!');
|
||||
} else {
|
||||
// Perform the final action here (e.g., submit form, complete registration)
|
||||
alert('Registration Complete');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={styles.rowContainer}>
|
||||
<div style={styles.col6}>
|
||||
<StepComponent activeStep={2} /> {/* Displays step 3 as active */}
|
||||
</div>
|
||||
<div style={styles.col6}>
|
||||
<p>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.</p>
|
||||
|
||||
<div style={styles.checkboxContainer}>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="agreement"
|
||||
style={styles.checkbox}
|
||||
checked={isChecked}
|
||||
onChange={handleCheckboxChange}
|
||||
/>
|
||||
<label htmlFor="agreement" style={styles.checkboxLabel}>
|
||||
Verify Your Business on Facebook
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{errorMessage && (
|
||||
<p style={{ color: 'red', fontSize: '0.875rem' }}>{errorMessage}</p>
|
||||
)}
|
||||
|
||||
<button onClick={onBack} style={styles.backButton}>
|
||||
Back
|
||||
</button>
|
||||
<button onClick={handleFinish} style={styles.startButton}>
|
||||
Finish
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Main Component
|
||||
const RegisterStep = () => {
|
||||
const [activeStep, setActiveStep] = useState(0);
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const handleStartNow = () => {
|
||||
setActiveStep(1); // Move to the second step
|
||||
};
|
||||
|
||||
const handleNextStep = () => {
|
||||
setActiveStep(activeStep + 1); // Move to the next step
|
||||
};
|
||||
|
||||
const handleBack = () => {
|
||||
setActiveStep(activeStep - 1); // Move to the previous step
|
||||
};
|
||||
|
||||
const responsiveStyles = isMobile
|
||||
? { flexDirection: 'column', alignItems: 'flex-start' }
|
||||
: { flexDirection: 'row' };
|
||||
|
||||
return (
|
||||
<div>
|
||||
<HeaderComponent />
|
||||
<div style={{ ...styles.rowContainer, ...responsiveStyles }}>
|
||||
{activeStep === 0 && (
|
||||
<FirstContentComponent onStartNow={handleStartNow} />
|
||||
)}
|
||||
{activeStep === 1 && (
|
||||
<SecondContentComponent onNextStep={handleNextStep} onBack={handleBack} />
|
||||
)}
|
||||
{activeStep === 2 && (
|
||||
<LastContentComponent onBack={handleBack} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
export default RegisterStep
|
||||
|
||||
const styles = {
|
||||
rowContainer: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
marginTop: '20px',
|
||||
flexWrap: 'wrap',
|
||||
borderRadius: '8px',
|
||||
border: '1px solid #ddd',
|
||||
boxShadow: '0px 2px 8px rgba(0, 0, 0, 0.1)',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
minHeight: '200px',
|
||||
},
|
||||
col6: {
|
||||
flex: '1 1 48%', // Membagi ruang menjadi 2 kolom
|
||||
padding: '10px',
|
||||
boxSizing: 'border-box',
|
||||
},
|
||||
headerContainer: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginBottom: '20px',
|
||||
borderRadius: '8px',
|
||||
border: '1px solid #ddd',
|
||||
boxShadow: '0px 2px 8px rgba(0, 0, 0, 0.1)',
|
||||
justifyContent: 'center',
|
||||
width: '100%',
|
||||
minHeight: '250px',
|
||||
},
|
||||
leftSideContainer: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: '10px',
|
||||
},
|
||||
image: (width, height) => ({
|
||||
width: width,
|
||||
height: height,
|
||||
flex: '1 1 33%',
|
||||
}),
|
||||
textContainer: {
|
||||
flex: '1 1 66%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-end',
|
||||
paddingLeft: '20px',
|
||||
},
|
||||
textTitle: {
|
||||
fontSize: '24px',
|
||||
fontWeight: 'bold',
|
||||
marginBottom: '10px',
|
||||
},
|
||||
textBody: {
|
||||
fontSize: '16px',
|
||||
lineHeight: '1.5',
|
||||
},
|
||||
// Step Styles
|
||||
stepsContainer: {
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
gap: '10px', // Jarak antar langkah
|
||||
position: 'relative',
|
||||
},
|
||||
step: {
|
||||
flex: '1 1 16.6%', // Membagi langkah dalam 1/6 bagian dari lebar container
|
||||
padding: '10px',
|
||||
boxSizing: 'border-box',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
textAlign: 'center',
|
||||
marginBottom: '10px',
|
||||
position: 'relative',
|
||||
},
|
||||
stepNumberContainer: {
|
||||
width: '40px',
|
||||
height: '40px',
|
||||
borderRadius: '50%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginBottom: '8px',
|
||||
transition: 'background-color 0.3s ease',
|
||||
},
|
||||
stepNumber: {
|
||||
fontSize: '16px',
|
||||
color: '#fff',
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
stepDescription: {
|
||||
fontSize: '14px',
|
||||
lineHeight: '1.5',
|
||||
color: '#333',
|
||||
textAlign: 'center',
|
||||
margin: '10px 0',
|
||||
},
|
||||
// Content Styles
|
||||
contentContainer: {
|
||||
marginTop: '20px',
|
||||
padding: '20px',
|
||||
},
|
||||
infoBox: {
|
||||
padding: '20px',
|
||||
marginBottom: '20px',
|
||||
},
|
||||
descriptionList: {
|
||||
listStyleType: 'disc',
|
||||
paddingLeft: '20px',
|
||||
color: '#333',
|
||||
fontSize: '14px',
|
||||
},
|
||||
descriptionText: {
|
||||
marginLeft: '0',
|
||||
paddingLeft: '0',
|
||||
fontSize: '14px',
|
||||
color: '#333',
|
||||
},
|
||||
startButton: {
|
||||
backgroundColor: '#0542cc',
|
||||
color: '#fff',
|
||||
padding: '10px 20px',
|
||||
border: 'none',
|
||||
borderRadius: '5px',
|
||||
cursor: 'pointer',
|
||||
marginTop: '15px',
|
||||
},
|
||||
checkboxContainer: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
marginBottom: '10px',
|
||||
},
|
||||
checkbox: {
|
||||
marginRight: '10px',
|
||||
},
|
||||
checkboxLabel: {
|
||||
color: '#0542cc',
|
||||
fontSize: '14px',
|
||||
},
|
||||
backButton: {
|
||||
padding: '10px 20px',
|
||||
backgroundColor: '#fff', // Background color updated to white
|
||||
color: '#000', // Text color set to black
|
||||
border: '2px solid #0542cc', // Border color set to #0542cc
|
||||
borderRadius: '5px',
|
||||
cursor: 'pointer',
|
||||
marginRight: '10px',
|
||||
},
|
||||
|
||||
};
|
||||
9
src/screens/Wa/Manage/Content/index.js
Normal file
9
src/screens/Wa/Manage/Content/index.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import RegisterContent from "./RegisterContent";
|
||||
import RegisterStep from "./RegisterStep";
|
||||
import CreateSettings from "./CreateSettings";
|
||||
|
||||
export {
|
||||
RegisterContent,
|
||||
RegisterStep,
|
||||
CreateSettings
|
||||
}
|
||||
437
src/screens/Wa/Manage/Integration.jsx
Normal file
437
src/screens/Wa/Manage/Integration.jsx
Normal file
@@ -0,0 +1,437 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
FaChevronLeft,
|
||||
FaChevronRight,
|
||||
FaFastBackward,
|
||||
FaFastForward,
|
||||
FaSort,
|
||||
FaSortUp,
|
||||
FaSortDown,
|
||||
FaEdit,
|
||||
FaEye,
|
||||
FaTrash
|
||||
} from 'react-icons/fa'; // Icons for sorting
|
||||
import { NoAvailable } from '../../../assets/icon';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
// Pagination Component
|
||||
const Pagination = ({ currentPage, totalPages, onPageChange }) => {
|
||||
const handlePrev = () => {
|
||||
if (currentPage > 1) {
|
||||
onPageChange(currentPage - 1);
|
||||
}
|
||||
};
|
||||
|
||||
const handleNext = () => {
|
||||
if (currentPage < totalPages) {
|
||||
onPageChange(currentPage + 1);
|
||||
}
|
||||
};
|
||||
|
||||
const handleFirst = () => {
|
||||
onPageChange(1); // Go to first page
|
||||
};
|
||||
|
||||
const handleLast = () => {
|
||||
onPageChange(totalPages); // Go to last page
|
||||
};
|
||||
|
||||
// Logic to display only 3 pages in pagination
|
||||
const getPaginationRange = () => {
|
||||
const range = [];
|
||||
const totalPagesCount = totalPages;
|
||||
|
||||
let start = currentPage - 1;
|
||||
let end = currentPage + 1;
|
||||
|
||||
// Adjust start and end if near the boundaries
|
||||
if (currentPage === 1) {
|
||||
start = 1;
|
||||
end = Math.min(3, totalPagesCount);
|
||||
} else if (currentPage === totalPages) {
|
||||
start = Math.max(totalPagesCount - 2, 1);
|
||||
end = totalPagesCount;
|
||||
}
|
||||
|
||||
for (let i = start; i <= end; i++) {
|
||||
range.push(i);
|
||||
}
|
||||
|
||||
return range;
|
||||
};
|
||||
|
||||
const pageRange = getPaginationRange();
|
||||
|
||||
return (
|
||||
<div className="pagination-container d-flex justify-content-end mt-4">
|
||||
{/* First Page Button */}
|
||||
<button
|
||||
className="btn"
|
||||
onClick={handleFirst}
|
||||
disabled={currentPage === 1}
|
||||
>
|
||||
<FaFastBackward /> {/* Double Arrow Left */}
|
||||
</button>
|
||||
|
||||
<button
|
||||
className="btn"
|
||||
onClick={handlePrev}
|
||||
disabled={currentPage === 1}
|
||||
>
|
||||
<FaChevronLeft /> {/* Single Arrow Left */}
|
||||
</button>
|
||||
|
||||
{/* Page Numbers */}
|
||||
{pageRange.map((pageNum) => (
|
||||
<button
|
||||
key={pageNum}
|
||||
className={`btn ${pageNum === currentPage ? 'btn-primary' : ''}`}
|
||||
onClick={() => onPageChange(pageNum)}
|
||||
>
|
||||
{pageNum}
|
||||
</button>
|
||||
))}
|
||||
|
||||
<button
|
||||
className="btn"
|
||||
onClick={handleNext}
|
||||
disabled={currentPage === totalPages}
|
||||
>
|
||||
<FaChevronRight /> {/* Single Arrow Right */}
|
||||
</button>
|
||||
|
||||
{/* Last Page Button */}
|
||||
<button
|
||||
className="btn"
|
||||
onClick={handleLast}
|
||||
disabled={currentPage === totalPages}
|
||||
>
|
||||
<FaFastForward /> {/* Double Arrow Right */}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
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 (
|
||||
<div className="container mt-5">
|
||||
{/* Welcome Message */}
|
||||
<div className="row-card border-left border-primary shadow mb-4" style={{ backgroundColor: '#E2FBEA' }}>
|
||||
<div className="d-flex flex-column justify-content-start align-items-start p-4">
|
||||
<div>
|
||||
<h4 className="mb-3 text-start">
|
||||
<i className="fas fa-warning fa-bold me-3"></i>Alert
|
||||
</h4>
|
||||
<p className="mb-0 text-start">
|
||||
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.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={styles.contentContainer}>
|
||||
{/* Filter Form */}
|
||||
<div className="card p-3 mb-4">
|
||||
<div className="row d-flex justify-content-around"> {/* Using Flexbox */}
|
||||
|
||||
{/* Dropdown: Business Account */}
|
||||
<div className={`col-12 ${isMobile ? 'mb-2' : 'col-md-2'}`}>
|
||||
<select className="form-control">
|
||||
<option>Select Application</option>
|
||||
<option>App 1</option>
|
||||
<option>App 2</option>
|
||||
<option>App 3</option>
|
||||
<option>App 4</option>
|
||||
<option>App 5</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Dropdown: Status */}
|
||||
<div className={`col-sm-12 ${isMobile ? 'mb-2' : 'col-md-2'}`}>
|
||||
<select className="form-control">
|
||||
<option>Select Status</option>
|
||||
<option>Status 1</option>
|
||||
<option>Status 2</option>
|
||||
<option>Status 3</option>
|
||||
<option>Status 4</option>
|
||||
<option>Status 5</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Apply and Cancel Buttons */}
|
||||
<div className={`col-12 ${isMobile ? 'd-flex justify-content-between' : 'col-md-2 d-flex align-items-end'}`} style={{ gap: '10px' }}>
|
||||
<button className="btn btn-primary w-48">Apply</button>
|
||||
<button className="btn btn-secondary w-48">Cancel</button>
|
||||
</div>
|
||||
|
||||
{/* Create Settings Button with Icon */}
|
||||
<div className={`col-12 ${isMobile ? 'd-flex justify-content-end' : 'col-md-2 d-flex justify-content-end align-items-end'}`} style={{ gap: '10px' }}>
|
||||
<Link to="/wa-createSettings" style={{ textDecoration: 'none' }}>
|
||||
<button className="btn btn-primary">
|
||||
<i className="fas fa-cogs me-2"></i> {/* Font Awesome icon */}
|
||||
Create Settings
|
||||
</button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Table */}
|
||||
<div className="table-responsive">
|
||||
<table className="table table-bordered" style={styles.tableContainer}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>No.</th> {/* Kolom untuk Nomor Urut */}
|
||||
<th>
|
||||
<button className="btn" onClick={() => handleSort('channelName')} style={styles.buttonStyle}>
|
||||
<span>Channel Name</span>
|
||||
{sortConfig.key === 'channelName' &&
|
||||
(sortConfig.direction === 'asc' ?
|
||||
<FaSortUp style={styles.iconMarginLeft} /> :
|
||||
<FaSortDown style={styles.iconMarginLeft} />
|
||||
)
|
||||
}
|
||||
{sortConfig.key !== 'channelName' && <FaSort style={styles.iconMarginLeft} />}
|
||||
</button>
|
||||
</th>
|
||||
<th>
|
||||
<button className="btn" onClick={() => handleSort('channelId')} style={styles.buttonStyle}>
|
||||
Channel ID
|
||||
{sortConfig.key === 'channelId' &&
|
||||
(sortConfig.direction === 'asc' ? <FaSortUp style={styles.iconMarginLeft} /> : <FaSortDown style={styles.iconMarginLeft} />)
|
||||
}
|
||||
{sortConfig.key !== 'channelId' && <FaSort style={styles.iconMarginLeft} />}
|
||||
</button>
|
||||
</th>
|
||||
<th>
|
||||
<button className="btn" onClick={() => handleSort('phoneNumber')} style={styles.buttonStyle}>
|
||||
Phone Number
|
||||
{sortConfig.key === 'phoneNumber' &&
|
||||
(sortConfig.direction === 'asc' ? <FaSortUp style={styles.iconMarginLeft} /> : <FaSortDown style={styles.iconMarginLeft} />)
|
||||
}
|
||||
{sortConfig.key !== 'phoneNumber' && <FaSort style={styles.iconMarginLeft} />}
|
||||
</button>
|
||||
</th>
|
||||
<th>
|
||||
<button className="btn" onClick={() => handleSort('status')} style={styles.buttonStyle}>
|
||||
Status
|
||||
{sortConfig.key === 'status' &&
|
||||
(sortConfig.direction === 'asc' ? <FaSortUp style={styles.iconMarginLeft} /> : <FaSortDown style={styles.iconMarginLeft} />)
|
||||
}
|
||||
{sortConfig.key !== 'status' && <FaSort style={styles.iconMarginLeft} />}
|
||||
</button>
|
||||
</th>
|
||||
<th>
|
||||
<button className="btn" onClick={() => handleSort('creationDate')} style={styles.buttonStyle}>
|
||||
Creation Date
|
||||
{sortConfig.key === 'creationDate' &&
|
||||
(sortConfig.direction === 'asc' ? <FaSortUp style={styles.iconMarginLeft} /> : <FaSortDown style={styles.iconMarginLeft} />)
|
||||
}
|
||||
{sortConfig.key !== 'creationDate' && <FaSort style={styles.iconMarginLeft} />}
|
||||
</button>
|
||||
</th>
|
||||
<th>
|
||||
<button className="btn" onClick={() => handleSort('tier')} style={styles.buttonStyle}>
|
||||
Tier
|
||||
{sortConfig.key === 'tier' &&
|
||||
(sortConfig.direction === 'asc' ? <FaSortUp style={styles.iconMarginLeft} /> : <FaSortDown style={styles.iconMarginLeft} />)
|
||||
}
|
||||
{sortConfig.key !== 'tier' && <FaSort style={styles.iconMarginLeft} />}
|
||||
</button>
|
||||
</th>
|
||||
<th>
|
||||
<button className="btn" onClick={() => handleSort('officialBusinessAccount')} style={styles.buttonStyle}>
|
||||
Official Business Account
|
||||
{sortConfig.key === 'officialBusinessAccount' &&
|
||||
(sortConfig.direction === 'asc' ? <FaSortUp style={styles.iconMarginLeft} /> : <FaSortDown style={styles.iconMarginLeft} />)
|
||||
}
|
||||
{sortConfig.key !== 'officialBusinessAccount' && <FaSort style={styles.iconMarginLeft} />}
|
||||
</button>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{paginatedData.length > 0 ? (
|
||||
paginatedData.map((transaction, index) => (
|
||||
<tr key={index}>
|
||||
{/* Kolom Nomor Urut */}
|
||||
<td>{(currentPage - 1) * dataPerPage + index + 1}</td> {/* Nomor urut berdasarkan halaman dan index */}
|
||||
<td>{transaction.channelName}</td>
|
||||
<td>{transaction.channelId}</td>
|
||||
<td>{transaction.phoneNumber}</td>
|
||||
<td>{transaction.status}</td>
|
||||
<td>{transaction.creationDate}</td>
|
||||
<td>{transaction.tier}</td>
|
||||
<td>{transaction.officialBusinessAccount}</td>
|
||||
</tr>
|
||||
))
|
||||
) : (
|
||||
<tr>
|
||||
<td colSpan="7" className="text-center">
|
||||
<div className="d-flex flex-column align-items-center mt-5">
|
||||
<img src={NoAvailable} alt="No Data Available" className="mb-3" style={styles.iconStyle} />
|
||||
<p>Data not available</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/* Pagination */}
|
||||
<Pagination
|
||||
currentPage={currentPage}
|
||||
totalPages={totalPages}
|
||||
onPageChange={handlePageChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Integration;
|
||||
|
||||
const styles = {
|
||||
contentContainer: {
|
||||
padding: '20px',
|
||||
border: '0.1px solid rgba(0, 0, 0, 0.2)',
|
||||
borderLeft: '4px solid #0542cc',
|
||||
borderRadius: '10px',
|
||||
width: '100%',
|
||||
},
|
||||
tableContainer: {
|
||||
minHeight: '300px',
|
||||
maxHeight: '1500px',
|
||||
overflowY: 'auto',
|
||||
},
|
||||
iconStyle: {
|
||||
width: '50px',
|
||||
height: '50px',
|
||||
},
|
||||
// Add margin-left style for icons
|
||||
iconMarginLeft: {
|
||||
marginLeft: '0.5rem',
|
||||
verticalAlign: 'middle',
|
||||
},
|
||||
buttonStyle: {
|
||||
display: 'inline-flex',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'center',
|
||||
padding: '0.5rem 1rem',
|
||||
},
|
||||
};
|
||||
|
||||
486
src/screens/Wa/Manage/Profile.jsx
Normal file
486
src/screens/Wa/Manage/Profile.jsx
Normal file
@@ -0,0 +1,486 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
FaChevronLeft,
|
||||
FaChevronRight,
|
||||
FaFastBackward,
|
||||
FaFastForward,
|
||||
FaSort,
|
||||
FaSortUp,
|
||||
FaSortDown,
|
||||
FaEdit,
|
||||
FaEye,
|
||||
FaTrash
|
||||
} from 'react-icons/fa'; // Icons for sorting
|
||||
import { NoAvailable } from '../../../assets/icon';
|
||||
|
||||
// Pagination Component
|
||||
const Pagination = ({ currentPage, totalPages, onPageChange }) => {
|
||||
const handlePrev = () => {
|
||||
if (currentPage > 1) {
|
||||
onPageChange(currentPage - 1);
|
||||
}
|
||||
};
|
||||
|
||||
const handleNext = () => {
|
||||
if (currentPage < totalPages) {
|
||||
onPageChange(currentPage + 1);
|
||||
}
|
||||
};
|
||||
|
||||
const handleFirst = () => {
|
||||
onPageChange(1); // Go to first page
|
||||
};
|
||||
|
||||
const handleLast = () => {
|
||||
onPageChange(totalPages); // Go to last page
|
||||
};
|
||||
|
||||
// Logic to display only 3 pages in pagination
|
||||
const getPaginationRange = () => {
|
||||
const range = [];
|
||||
const totalPagesCount = totalPages;
|
||||
|
||||
let start = currentPage - 1;
|
||||
let end = currentPage + 1;
|
||||
|
||||
// Adjust start and end if near the boundaries
|
||||
if (currentPage === 1) {
|
||||
start = 1;
|
||||
end = Math.min(3, totalPagesCount);
|
||||
} else if (currentPage === totalPages) {
|
||||
start = Math.max(totalPagesCount - 2, 1);
|
||||
end = totalPagesCount;
|
||||
}
|
||||
|
||||
for (let i = start; i <= end; i++) {
|
||||
range.push(i);
|
||||
}
|
||||
|
||||
return range;
|
||||
};
|
||||
|
||||
const pageRange = getPaginationRange();
|
||||
|
||||
return (
|
||||
<div className="pagination-container d-flex justify-content-end mt-4">
|
||||
{/* First Page Button */}
|
||||
<button
|
||||
className="btn"
|
||||
onClick={handleFirst}
|
||||
disabled={currentPage === 1}
|
||||
>
|
||||
<FaFastBackward /> {/* Double Arrow Left */}
|
||||
</button>
|
||||
|
||||
<button
|
||||
className="btn"
|
||||
onClick={handlePrev}
|
||||
disabled={currentPage === 1}
|
||||
>
|
||||
<FaChevronLeft /> {/* Single Arrow Left */}
|
||||
</button>
|
||||
|
||||
{/* Page Numbers */}
|
||||
{pageRange.map((pageNum) => (
|
||||
<button
|
||||
key={pageNum}
|
||||
className={`btn ${pageNum === currentPage ? 'btn-primary' : ''}`}
|
||||
onClick={() => onPageChange(pageNum)}
|
||||
>
|
||||
{pageNum}
|
||||
</button>
|
||||
))}
|
||||
|
||||
<button
|
||||
className="btn"
|
||||
onClick={handleNext}
|
||||
disabled={currentPage === totalPages}
|
||||
>
|
||||
<FaChevronRight /> {/* Single Arrow Right */}
|
||||
</button>
|
||||
|
||||
{/* Last Page Button */}
|
||||
<button
|
||||
className="btn"
|
||||
onClick={handleLast}
|
||||
disabled={currentPage === totalPages}
|
||||
>
|
||||
<FaFastForward /> {/* Double Arrow Right */}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
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 (
|
||||
<div className="container mt-5">
|
||||
{/* Welcome Message */}
|
||||
<div className="row-card border-left border-primary shadow mb-4" style={{ backgroundColor: '#E2FBEA' }}>
|
||||
<div className="d-flex flex-column justify-content-start align-items-start p-4">
|
||||
<div>
|
||||
<h4 className="mb-3 text-start">
|
||||
<i className="fas fa-warning fa-bold me-3"></i>Alert
|
||||
</h4>
|
||||
<p className="mb-0 text-start">
|
||||
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.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={styles.contentContainer}>
|
||||
{/* Filter Form */}
|
||||
<div className="card p-3 mb-4">
|
||||
<div className="row d-flex justify-content-arround"> {/* Menggunakan Flexbox */}
|
||||
{/* Dropdown: Business Account */}
|
||||
<div className={`col-sm-12 ${isMobile ? 'mb-2' : 'col-md-2'}`}>
|
||||
<select className="form-control">
|
||||
<option>Select Business Account</option>
|
||||
<option>Account 1</option>
|
||||
<option>Account 2</option>
|
||||
<option>Account 3</option>
|
||||
<option>Account 4</option>
|
||||
<option>Account 5</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Input: Business Account Name */}
|
||||
<div className={`col-sm-12 ${isMobile ? 'mb-2' : 'col-md-2'}`}>
|
||||
<input type="text" className="form-control" placeholder="Enter Business Account Name" />
|
||||
</div>
|
||||
|
||||
{/* Dropdown: Meta Manager ID */}
|
||||
<div className={`col-sm-12 ${isMobile ? 'mb-2' : 'col-md-2'}`}>
|
||||
<select className="form-control">
|
||||
<option>Select Meta Manager</option>
|
||||
<option>Manager 1</option>
|
||||
<option>Manager 2</option>
|
||||
<option>Manager 3</option>
|
||||
<option>Manager 4</option>
|
||||
<option>Manager 5</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Dropdown: WABA ID */}
|
||||
<div className={`col-sm-12 ${isMobile ? 'mb-2' : 'col-md-2'}`}>
|
||||
<select className="form-control">
|
||||
<option>Select WABA ID</option>
|
||||
<option>WABA 1</option>
|
||||
<option>WABA 2</option>
|
||||
<option>WABA 3</option>
|
||||
<option>WABA 4</option>
|
||||
<option>WABA 5</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{/* Action Buttons */}
|
||||
<div className="d-flex justify-content-between align-items-center mb-3">
|
||||
<div>
|
||||
{buttonData.map((button, index) =>
|
||||
button.enabled ? (
|
||||
<button
|
||||
key={index}
|
||||
className={`btn btn-light ${isMobile ? 'mb-2' : ''}`} // Add margin on mobile
|
||||
style={styles.actionButton}
|
||||
>
|
||||
{button.label}
|
||||
</button>
|
||||
) : null
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Search Bar with Icon */}
|
||||
<div className="input-group" style={{ width: '250px', display: 'flex', alignItems: 'center', justifyContent: 'flex-end' }}>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search..."
|
||||
className="form-control"
|
||||
/>
|
||||
<span className="input-group-text">
|
||||
<i className="fas fa-search"></i> {/* FontAwesome search icon */}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{/* Table */}
|
||||
<div className="table-responsive">
|
||||
<table className="table table-bordered" style={styles.tableContainer}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>No.</th> {/* Kolom untuk Nomor Urut */}
|
||||
<th>
|
||||
<button className="btn" onClick={() => handleSort('channelName')} style={styles.buttonStyle}>
|
||||
<span>Channel Name</span>
|
||||
{sortConfig.key === 'channelName' &&
|
||||
(sortConfig.direction === 'asc' ?
|
||||
<FaSortUp style={styles.iconMarginLeft} /> :
|
||||
<FaSortDown style={styles.iconMarginLeft} />
|
||||
)
|
||||
}
|
||||
{sortConfig.key !== 'channelName' && <FaSort style={styles.iconMarginLeft} />}
|
||||
</button>
|
||||
</th>
|
||||
<th>
|
||||
<button className="btn" onClick={() => handleSort('channelId')} style={styles.buttonStyle}>
|
||||
Channel ID
|
||||
{sortConfig.key === 'channelId' &&
|
||||
(sortConfig.direction === 'asc' ? <FaSortUp style={styles.iconMarginLeft} /> : <FaSortDown style={styles.iconMarginLeft} />)
|
||||
}
|
||||
{sortConfig.key !== 'channelId' && <FaSort style={styles.iconMarginLeft} />}
|
||||
</button>
|
||||
</th>
|
||||
<th>
|
||||
<button className="btn" onClick={() => handleSort('phoneNumber')} style={styles.buttonStyle}>
|
||||
Phone Number
|
||||
{sortConfig.key === 'phoneNumber' &&
|
||||
(sortConfig.direction === 'asc' ? <FaSortUp style={styles.iconMarginLeft} /> : <FaSortDown style={styles.iconMarginLeft} />)
|
||||
}
|
||||
{sortConfig.key !== 'phoneNumber' && <FaSort style={styles.iconMarginLeft} />}
|
||||
</button>
|
||||
</th>
|
||||
<th>
|
||||
<button className="btn" onClick={() => handleSort('status')} style={styles.buttonStyle}>
|
||||
Status
|
||||
{sortConfig.key === 'status' &&
|
||||
(sortConfig.direction === 'asc' ? <FaSortUp style={styles.iconMarginLeft} /> : <FaSortDown style={styles.iconMarginLeft} />)
|
||||
}
|
||||
{sortConfig.key !== 'status' && <FaSort style={styles.iconMarginLeft} />}
|
||||
</button>
|
||||
</th>
|
||||
<th>
|
||||
<button className="btn" onClick={() => handleSort('creationDate')} style={styles.buttonStyle}>
|
||||
Creation Date
|
||||
{sortConfig.key === 'creationDate' &&
|
||||
(sortConfig.direction === 'asc' ? <FaSortUp style={styles.iconMarginLeft} /> : <FaSortDown style={styles.iconMarginLeft} />)
|
||||
}
|
||||
{sortConfig.key !== 'creationDate' && <FaSort style={styles.iconMarginLeft} />}
|
||||
</button>
|
||||
</th>
|
||||
<th>
|
||||
<button className="btn" onClick={() => handleSort('officialBusinessAccount')} style={styles.buttonStyle}>
|
||||
Official Business Account
|
||||
{sortConfig.key === 'officialBusinessAccount' &&
|
||||
(sortConfig.direction === 'asc' ? <FaSortUp style={styles.iconMarginLeft} /> : <FaSortDown style={styles.iconMarginLeft} />)
|
||||
}
|
||||
{sortConfig.key !== 'officialBusinessAccount' && <FaSort style={styles.iconMarginLeft} />}
|
||||
</button>
|
||||
</th>
|
||||
<th style={{ textAlign: 'center', verticalAlign: 'middle' }}>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{paginatedData.length > 0 ? (
|
||||
paginatedData.map((transaction, index) => (
|
||||
<tr key={index}>
|
||||
{/* Kolom Nomor Urut */}
|
||||
<td>{(currentPage - 1) * dataPerPage + index + 1}</td> {/* Nomor urut berdasarkan halaman dan index */}
|
||||
<td>{transaction.channelName}</td>
|
||||
<td>{transaction.channelId}</td>
|
||||
<td>{transaction.phoneNumber}</td>
|
||||
<td>{transaction.status}</td>
|
||||
<td>{transaction.creationDate}</td>
|
||||
<td>{transaction.officialBusinessAccount}</td>
|
||||
<td style={{ textAlign: 'center' }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'center', gap: '10px' }}>
|
||||
<button
|
||||
className="btn btn-warning"
|
||||
style={{ display: 'flex', alignItems: 'center' }}
|
||||
onClick={() => handleEdit(transaction.transactionId)}
|
||||
>
|
||||
<FaEdit style={{ marginRight: '5px' }} />
|
||||
Edit
|
||||
</button>
|
||||
|
||||
<button
|
||||
className="btn btn-success"
|
||||
style={{ display: 'flex', alignItems: 'center' }}
|
||||
onClick={() => handleShow(transaction.transactionId)}
|
||||
>
|
||||
<FaEye style={{ marginRight: '5px' }} />
|
||||
Show
|
||||
</button>
|
||||
|
||||
<button
|
||||
className="btn btn-danger"
|
||||
style={{ display: 'flex', alignItems: 'center' }}
|
||||
onClick={() => handleDelete(transaction.transactionId)}
|
||||
>
|
||||
<FaTrash style={{ marginRight: '5px' }} />
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
) : (
|
||||
<tr>
|
||||
<td colSpan="7" className="text-center">
|
||||
<div className="d-flex flex-column align-items-center mt-5">
|
||||
<img src={NoAvailable} alt="No Data Available" className="mb-3" style={styles.iconStyle} />
|
||||
<p>Data not available</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/* Pagination */}
|
||||
<Pagination
|
||||
currentPage={currentPage}
|
||||
totalPages={totalPages}
|
||||
onPageChange={handlePageChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Profile;
|
||||
|
||||
const styles = {
|
||||
contentContainer: {
|
||||
padding: '20px',
|
||||
border: '0.1px solid rgba(0, 0, 0, 0.2)',
|
||||
borderLeft: '4px solid #0542cc',
|
||||
borderRadius: '10px',
|
||||
width: '100%',
|
||||
},
|
||||
tableContainer: {
|
||||
minHeight: '300px',
|
||||
maxHeight: '1500px',
|
||||
overflowY: 'auto',
|
||||
},
|
||||
iconStyle: {
|
||||
width: '50px',
|
||||
height: '50px',
|
||||
},
|
||||
// Add margin-left style for icons
|
||||
iconMarginLeft: {
|
||||
marginLeft: '0.5rem',
|
||||
verticalAlign: 'middle',
|
||||
},
|
||||
buttonStyle: {
|
||||
display: 'inline-flex',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'center',
|
||||
padding: '0.5rem 1rem',
|
||||
},
|
||||
};
|
||||
|
||||
29
src/screens/Wa/Manage/Registration.jsx
Normal file
29
src/screens/Wa/Manage/Registration.jsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import React from 'react'
|
||||
import { RegisterContent } from './Content'
|
||||
|
||||
const Registration = () => {
|
||||
return (
|
||||
<div className='container' style={styles.container}>
|
||||
<div style={styles.section}>
|
||||
<h1>WA Manage</h1>
|
||||
<RegisterContent />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
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%',
|
||||
},
|
||||
}
|
||||
346
src/screens/Wa/Manage/Template.jsx
Normal file
346
src/screens/Wa/Manage/Template.jsx
Normal file
@@ -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 (
|
||||
<div style={styles.container}>
|
||||
{/* Welcome Message */}
|
||||
<div className="row-card border-left border-primary shadow mb-4" style={styles.welcomeCard}>
|
||||
<div className="d-flex flex-column justify-content-start align-items-start p-4">
|
||||
<div>
|
||||
<h4 className="mb-3 text-start">
|
||||
<i className="fas fa-message fa-bold me-3"></i>Create WhatsApp Business Templates
|
||||
</h4>
|
||||
<p className="mb-0 text-start">
|
||||
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.
|
||||
</p>
|
||||
</div>
|
||||
<div className="d-flex flex-row mt-3">
|
||||
<Link to="/" style={{ textDecoration: 'none' }}>
|
||||
<button className="btn d-flex justify-content-center align-items-center me-2" style={styles.createButton}>
|
||||
<i className="fas fa-plus text-white me-2"></i>
|
||||
<p className="text-white mb-0">Create Template</p>
|
||||
</button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={styles.wrapperCard}>
|
||||
<div className="card mt-3">
|
||||
<div className="row">
|
||||
{/* Filtering Options */}
|
||||
<div className="col-md-2">
|
||||
<select className="form-control">
|
||||
<option value="">Pilih WABA IDI</option>
|
||||
<option value="waba1">WABA 1</option>
|
||||
<option value="waba2">WABA 2</option>
|
||||
<option value="waba3">WABA 3</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="col-md-2">
|
||||
<select className="form-control">
|
||||
<option value="">Pilih Kategori</option>
|
||||
<option value="kategori1">Kategori 1</option>
|
||||
<option value="kategori2">Kategori 2</option>
|
||||
<option value="kategori3">Kategori 3</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="col-md-2">
|
||||
<select className="form-control">
|
||||
<option value="">Pilih Status</option>
|
||||
<option value="aktif">Aktif</option>
|
||||
<option value="non-aktif">Non-Aktif</option>
|
||||
<option value="pending">Pending</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="col-md-2 d-flex align-items-end" style={{ gap: '10px' }}>
|
||||
<button className="btn btn-primary w-100">Apply</button>
|
||||
<button className="btn btn-secondary w-100">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Search Bar */}
|
||||
<div style={styles.searchInputWrapper}>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Type your keywords here"
|
||||
style={styles.searchInput}
|
||||
value={searchTerm}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<button style={styles.searchButton}>
|
||||
<i className="fas fa-search" style={styles.searchIcon}></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Application Cards */}
|
||||
<div style={styles.cardContainer}>
|
||||
{/* Show message if no data or search result */}
|
||||
{ownership.length === 0 && !errorMessage && <p style={styles.noDataMessage}>Data is not found.</p>}
|
||||
|
||||
{ownership.map((item, index) => (
|
||||
<div key={item.id} style={styles.card}>
|
||||
<div style={styles.cardHeader}>
|
||||
<span style={styles.cardTitle}>{item.name}</span>
|
||||
</div>
|
||||
<div style={styles.cardContent}>
|
||||
<div style={styles.applicationIdWrapper}>
|
||||
<p><strong>WABA Ownership:</strong></p>
|
||||
<p>{item.name}</p>
|
||||
</div>
|
||||
|
||||
{/* Copy to Clipboard Button */}
|
||||
<CopyToClipboard
|
||||
key={index}
|
||||
text={item.name}
|
||||
onCopy={() => {
|
||||
setCopiedIndex(index);
|
||||
setTimeout(() => setCopiedIndex(null), 2000); // Hide "Copied" status after 2 seconds
|
||||
}}
|
||||
>
|
||||
<button
|
||||
style={{
|
||||
backgroundColor: copiedIndex === index ? '#4BA5E7' : '#0542cc',
|
||||
padding: '10px 20px',
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
onMouseEnter={() => handleMouseEnter(index)}
|
||||
onMouseLeave={() => handleMouseLeave(index)}
|
||||
>
|
||||
{copiedIndex === index ? (
|
||||
<i className="fas fa-check"></i> // Show check icon if copied
|
||||
) : (
|
||||
<i className="fas fa-copy"></i> // Show copy icon
|
||||
)}
|
||||
{copiedIndex === index ? 'Copied' : 'Copy'}
|
||||
</button>
|
||||
</CopyToClipboard>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Show error message */}
|
||||
{errorMessage && <p style={styles.errorMessage}>{errorMessage}</p>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = {
|
||||
container: {
|
||||
margin: '20px auto',
|
||||
maxWidth: '1200px',
|
||||
},
|
||||
welcomeCard: {
|
||||
backgroundColor: '#F1F3F6',
|
||||
borderColor: '#1e2b34',
|
||||
borderRadius: '5px',
|
||||
marginBottom: '20px',
|
||||
},
|
||||
createButton: {
|
||||
backgroundColor: '#1d3f68',
|
||||
color: '#fff',
|
||||
borderRadius: '5px',
|
||||
padding: '12px 20px',
|
||||
width: '150px',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
wrapperCard: {
|
||||
padding: '20px',
|
||||
},
|
||||
searchInputWrapper: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
marginTop: '20px',
|
||||
marginBottom: '20px',
|
||||
},
|
||||
searchInput: {
|
||||
padding: '10px',
|
||||
border: '1px solid #ccc',
|
||||
borderRadius: '5px',
|
||||
width: '300px',
|
||||
marginRight: '10px',
|
||||
},
|
||||
searchButton: {
|
||||
backgroundColor: '#1d3f68',
|
||||
border: 'none',
|
||||
color: 'white',
|
||||
padding: '10px 15px',
|
||||
cursor: 'pointer',
|
||||
},
|
||||
searchIcon: {
|
||||
fontSize: '16px',
|
||||
},
|
||||
cardContainer: {
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))',
|
||||
gap: '20px',
|
||||
},
|
||||
card: {
|
||||
backgroundColor: '#fff',
|
||||
border: '1px solid #ddd',
|
||||
borderRadius: '10px',
|
||||
padding: '20px',
|
||||
boxShadow: '0 2px 4px rgba(0, 0, 0, 0.1)',
|
||||
},
|
||||
cardHeader: {
|
||||
marginBottom: '10px',
|
||||
},
|
||||
cardTitle: {
|
||||
fontSize: '18px',
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
cardContent: {
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
},
|
||||
applicationIdWrapper: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
},
|
||||
noDataMessage: {
|
||||
color: 'gray',
|
||||
},
|
||||
errorMessage: {
|
||||
color: 'red',
|
||||
}
|
||||
};
|
||||
|
||||
export default Template;
|
||||
11
src/screens/Wa/Manage/index.js
Normal file
11
src/screens/Wa/Manage/index.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import Registration from './Registration';
|
||||
import Profile from './Profile';
|
||||
import Template from './Template';
|
||||
import Integration from './Integration';
|
||||
|
||||
export {
|
||||
Registration,
|
||||
Profile,
|
||||
Template,
|
||||
Integration
|
||||
}
|
||||
Reference in New Issue
Block a user