Slicing UI

This commit is contained in:
2024-11-25 17:51:46 +07:00
parent 231c637fa9
commit 392a8a08fb
54 changed files with 8055 additions and 76 deletions

View 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',
},
};

View 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;

View 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',
},
};

View File

@@ -0,0 +1,9 @@
import RegisterContent from "./RegisterContent";
import RegisterStep from "./RegisterStep";
import CreateSettings from "./CreateSettings";
export {
RegisterContent,
RegisterStep,
CreateSettings
}

View 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',
},
};

View 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',
},
};

View 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%',
},
}

View 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;

View 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
}