347 lines
11 KiB
JavaScript
347 lines
11 KiB
JavaScript
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;
|