OTP-WA
This commit is contained in:
parent
5a23d77b23
commit
2f97160893
@ -113,6 +113,7 @@ const Compare = () => {
|
||||
};
|
||||
|
||||
const handleImageUpload = (file) => {
|
||||
setShowResult(false);
|
||||
if (!file) {
|
||||
setUploadError('Please select a file');
|
||||
return;
|
||||
@ -140,12 +141,15 @@ const Compare = () => {
|
||||
}
|
||||
|
||||
// Set file and filename if validation passes
|
||||
const previewUrl = URL.createObjectURL(file);
|
||||
setImageUrl(previewUrl);
|
||||
setFile(file);
|
||||
setSelectedImageName(file.name); // Add this line
|
||||
setUploadError(''); // Clear any previous errors
|
||||
setUploadError(''); // Clear any previous errors
|
||||
};
|
||||
|
||||
const handleCompareImageUpload = (file) => {
|
||||
setShowResult(false);
|
||||
if (!file) {
|
||||
setCompareUploadError('Please select a file');
|
||||
return;
|
||||
@ -173,6 +177,8 @@ const Compare = () => {
|
||||
}
|
||||
|
||||
// Set file and filename if validation passes
|
||||
const previewUrl = URL.createObjectURL(file);
|
||||
setImageCompareUrl(previewUrl);
|
||||
setCompareFile(file);
|
||||
setSelectedCompareImageName(file.name); // Add this line
|
||||
setCompareUploadError(''); // Clear any previous errors
|
||||
@ -181,13 +187,17 @@ const Compare = () => {
|
||||
|
||||
const handleImageCancel = () => {
|
||||
setSelectedImageName('');
|
||||
setFile(null);
|
||||
setImageUrl('');
|
||||
setShowResult(false);
|
||||
if (fileInputRef.current) fileInputRef.current.value = '';
|
||||
};
|
||||
|
||||
const handleCompareImageCancel = () => {
|
||||
setSelectedCompareImageName('');
|
||||
setCompareFile(null);
|
||||
setImageCompareUrl('');
|
||||
setShowResult(false);
|
||||
if (fileCompareInputRef.current) fileCompareInputRef.current.value = '';
|
||||
};
|
||||
|
||||
@ -534,6 +544,17 @@ const Compare = () => {
|
||||
{/* Display uploaded image name */}
|
||||
{selectedImageName && (
|
||||
<div className="mt-3">
|
||||
<img
|
||||
src={imageUrl}
|
||||
alt="Selected preview"
|
||||
style={{
|
||||
width: '100px',
|
||||
height: '100px',
|
||||
objectFit: 'cover',
|
||||
marginBottom: '10px',
|
||||
borderRadius: '4px'
|
||||
}}
|
||||
/>
|
||||
<p><strong>File:</strong> {selectedImageName}</p>
|
||||
{file && (
|
||||
<p style={styles.fileSize}>
|
||||
@ -583,6 +604,17 @@ const Compare = () => {
|
||||
{/* Display uploaded image name */}
|
||||
{selectedCompareImageName && (
|
||||
<div className="mt-3">
|
||||
<img
|
||||
src={imageCompareUrl}
|
||||
alt="Compare preview"
|
||||
style={{
|
||||
width: '100px',
|
||||
height: '100px',
|
||||
objectFit: 'cover',
|
||||
marginBottom: '10px',
|
||||
borderRadius: '4px'
|
||||
}}
|
||||
/>
|
||||
<p><strong>File:</strong> {selectedCompareImageName}</p>
|
||||
{compareFile && (
|
||||
<p style={styles.fileSize}>
|
||||
|
@ -606,6 +606,16 @@ const Enroll = () => {
|
||||
|
||||
{selectedImageName && (
|
||||
<div className="mt-3">
|
||||
<img
|
||||
src={imageUrl || "path-to-your-image"}
|
||||
alt="Contoh Foto"
|
||||
style={{
|
||||
maxWidth: '20%',
|
||||
height: 'auto',
|
||||
objectFit: 'contain',
|
||||
borderRadius: '5px'
|
||||
}}
|
||||
/>
|
||||
<p><strong>File:</strong> {selectedImageName}</p>
|
||||
<button className="btn btn-danger" onClick={handleImageCancel}>
|
||||
<FontAwesomeIcon icon={faTimes} className="me-2" />Cancel
|
||||
|
@ -128,12 +128,16 @@ const Search = () => {
|
||||
|
||||
// Add this after your existing state declarations
|
||||
const handleImageUpload = (file) => {
|
||||
setShowResult(false);
|
||||
if (!file) {
|
||||
setImageError('Please select a file');
|
||||
return;
|
||||
}
|
||||
|
||||
// Store the uploaded file
|
||||
const previewUrl = URL.createObjectURL(file);
|
||||
setImageUrls([previewUrl]);
|
||||
|
||||
setUploadedFile(file);
|
||||
setFile(file);
|
||||
setSelectedImageName(file.name);
|
||||
@ -160,9 +164,6 @@ const Search = () => {
|
||||
setUploadedFile(null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create preview URL
|
||||
const previewUrl = URL.createObjectURL(file);
|
||||
|
||||
// Check image dimensions
|
||||
const img = new Image();
|
||||
@ -752,6 +753,17 @@ const Search = () => {
|
||||
|
||||
{selectedImageName && (
|
||||
<div className="mt-3">
|
||||
<img
|
||||
src={imageUrls[0]}
|
||||
alt="Selected preview"
|
||||
style={{
|
||||
width: '100px',
|
||||
height: '100px',
|
||||
objectFit: 'cover',
|
||||
marginBottom: '10px',
|
||||
borderRadius: '4px'
|
||||
}}
|
||||
/>
|
||||
<p><strong>File:</strong> {selectedImageName}</p>
|
||||
{file && (
|
||||
<p style={styles.fileSize}>
|
||||
|
@ -795,6 +795,17 @@ const Verify = () => {
|
||||
{/* Display uploaded image name */}
|
||||
{selectedImageName && (
|
||||
<div className="mt-3">
|
||||
<img
|
||||
src={imageUrl || "path-to-your-image"}
|
||||
alt="Example Image"
|
||||
style={{
|
||||
maxWidth: '25%',
|
||||
width: 'auto',
|
||||
height: 'auto',
|
||||
objectFit: 'contain',
|
||||
borderRadius: '5px'
|
||||
}}
|
||||
/>
|
||||
<p><strong>File:</strong> {selectedImageName}</p>
|
||||
<button className="btn btn-danger" onClick={handleImageCancel}>
|
||||
<FontAwesomeIcon icon={faTimes} className="me-2" />Cancel
|
||||
|
@ -1,35 +1,60 @@
|
||||
import React, { useState, useRef, useEffect } from 'react'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faChevronLeft, faChevronDown } from '@fortawesome/free-solid-svg-icons';
|
||||
import { ServerDownAnimation } from '../../../../assets/images';
|
||||
import Select from 'react-select'
|
||||
|
||||
const BASE_URL = process.env.REACT_APP_BASE_URL
|
||||
const API_KEY = process.env.REACT_APP_API_KEY
|
||||
|
||||
const Verify = ({ phoneId, onVerify }) => {
|
||||
const Verify = ({ onVerify, generateId }) => {
|
||||
const [otpCode, setOtpCode] = useState('');
|
||||
const [generateCode, setGenerateCode] = useState('');
|
||||
const [generateCode, setGenerateCode] = useState(generateId);
|
||||
const [error, setError] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
setGenerateCode(generateId);
|
||||
}, [generateId]);
|
||||
|
||||
const handleClick = async () => {
|
||||
console.log('Verify Click');
|
||||
console.log('otp code: ', otpCode);
|
||||
console.log('generate code: ', generateCode);
|
||||
console.log('phone number: ', phoneId);
|
||||
|
||||
// Validation logic
|
||||
if (!otpCode) {
|
||||
setError("OTP Code is required."); // Set error message directly
|
||||
return; // Exit the function if there are errors
|
||||
} else if (otpCode.length !== 6) {
|
||||
setError("OTP Code must be exactly 6 characters long."); // Set error message directly
|
||||
return; // Exit the function if there are errors
|
||||
setError("OTP Code is required.");
|
||||
return;
|
||||
}
|
||||
|
||||
setError(''); // Clear error if validation passes
|
||||
onVerify(); // Call the onVerify function passed from Auth
|
||||
const requestData = {
|
||||
id: generateId,
|
||||
otp: otpCode
|
||||
};
|
||||
|
||||
fetch(`${BASE_URL}/wa/otp-verify`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-api-key': API_KEY
|
||||
},
|
||||
body: JSON.stringify(requestData)
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.status_code === 201) {
|
||||
setError(''); // Clear any existing error message
|
||||
alert('OTP verification successful!'); // Show success alert
|
||||
onVerify(); // Trigger verify callback
|
||||
} else {
|
||||
setError(data.details.message || "Verification failed");
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Verification failed:', error);
|
||||
setError("Failed to verify OTP");
|
||||
});
|
||||
};
|
||||
|
||||
const generateTimestampId = () => {
|
||||
const timestamp = new Date().getTime().toString(16); // Convert timestamp to hex
|
||||
const randomPart = Math.random().toString(16).substr(2, 8);
|
||||
return `${timestamp}-${randomPart}-${randomPart}-${randomPart}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={styles.section}>
|
||||
@ -41,29 +66,13 @@ const Verify = ({ phoneId, onVerify }) => {
|
||||
type="text"
|
||||
id="generateCode"
|
||||
className="form-control"
|
||||
placeholder="e133d538-dc40-4c43-b042-06aad864d634"
|
||||
value={generateCode}
|
||||
placeholder="Generated Code"
|
||||
value={`${generateTimestampId()}-${generateCode}`}
|
||||
onChange={(e) => setGenerateCode(e.target.value)}
|
||||
readOnly
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-md-6">
|
||||
<div className="input-group">
|
||||
<span className="input-group-prepend">
|
||||
<span className="input-group-text">+62</span>
|
||||
</span>
|
||||
<input
|
||||
type="text"
|
||||
id="phoneId"
|
||||
className="form-control"
|
||||
placeholder="Phone Number"
|
||||
value={phoneId}
|
||||
readOnly
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* OTP Code */}
|
||||
@ -77,14 +86,12 @@ const Verify = ({ phoneId, onVerify }) => {
|
||||
value={otpCode}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value;
|
||||
// Allow only digits and enforce exactly 6 digits
|
||||
if (/^\d{0,6}$/.test(value)) {
|
||||
setOtpCode(value);
|
||||
}
|
||||
}}
|
||||
maxLength={6} // Prevents entering more than 6 digits
|
||||
maxLength={6}
|
||||
/>
|
||||
|
||||
{error && <small className="text-danger">{error}</small>}
|
||||
</div>
|
||||
</div>
|
||||
@ -100,9 +107,24 @@ const Verify = ({ phoneId, onVerify }) => {
|
||||
}
|
||||
|
||||
|
||||
const Preview = () => {
|
||||
const Preview = ({ otpLength }) => {
|
||||
// Generate OTP when otpLength changes
|
||||
const generateOTP = (length) => {
|
||||
let otp = '';
|
||||
for (let i = 0; i < length; i++) {
|
||||
otp += Math.floor(Math.random() * 10);
|
||||
}
|
||||
return otp;
|
||||
};
|
||||
|
||||
// State to store the generated OTP
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
|
||||
// Update OTP whenever otpLength changes
|
||||
useEffect(() => {
|
||||
setInputValue(generateOTP(parseInt(otpLength))); // Re-generate OTP on otpLength change
|
||||
}, [otpLength]); // Dependency array with otpLength
|
||||
|
||||
const handleCopy = () => {
|
||||
navigator.clipboard.writeText(inputValue);
|
||||
console.log('Copied');
|
||||
@ -112,38 +134,36 @@ const Preview = () => {
|
||||
<div className="form-group row align-items-center mt-4">
|
||||
<div className="col-10">
|
||||
<div style={styles.selectWrapper}>
|
||||
<input
|
||||
type="text"
|
||||
id="inputValue"
|
||||
className="form-control"
|
||||
placeholder="7976 is your verification code. For your security, do not share this code."
|
||||
value={inputValue}
|
||||
onChange={(e) => setInputValue(e.target.value)}
|
||||
readOnly
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
id="inputValue"
|
||||
className="form-control"
|
||||
value={`${inputValue} is your verification code. For your security, do not share this code.`}
|
||||
onChange={(e) => setInputValue(e.target.value)}
|
||||
readOnly
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-2 d-flex">
|
||||
<div style={styles.selectWrapper}>
|
||||
<button className="btn btn-primary" onClick={handleCopy}>
|
||||
Copy
|
||||
</button>
|
||||
<button className="btn btn-primary" onClick={handleCopy}>
|
||||
Copy
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
const Auth = () => {
|
||||
|
||||
const [isSelectOpen, setIsSelectOpen] = useState(false);
|
||||
const [applicationId, setApplicationId] = useState('');
|
||||
const [expiryId, setExpiryId] = useState(0);
|
||||
const [otpId, setOtpId] = useState('');
|
||||
const [phoneId, setPhoneId] = useState('');
|
||||
const [templateId, setTemplateId] = useState('');
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const [errorMessage, setErrorMessage] = useState('');
|
||||
|
||||
const [applicationIds, setApplicationIds] = useState([]);
|
||||
const [showVerify, setShowVerify] = useState(false);
|
||||
@ -159,6 +179,9 @@ const Auth = () => {
|
||||
label: app.name
|
||||
}));
|
||||
|
||||
const [templateOptions, setTemplateOptions] = useState([]);
|
||||
const [generateId, setgenerateId] = useState('');
|
||||
|
||||
const handleApplicationChange = (selectedOption) => {
|
||||
const selectedId = selectedOption.value;
|
||||
const selectedApp = applicationIds.find(app => app.id === parseInt(selectedId));
|
||||
@ -170,58 +193,97 @@ const Auth = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleTemplateChange = (selectedOption) => {
|
||||
setTemplateId(selectedOption ? selectedOption.value : '');
|
||||
};
|
||||
|
||||
const handleInputChangeApplication = (newInputValue) => {
|
||||
// Limit input to 15 characters for Application ID
|
||||
if (newInputValue.length <= 15) {
|
||||
setInputValueApplication(newInputValue);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const fetchApplicationIds = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const response = await fetch(`${BASE_URL}/application/list`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'accept': 'application/json',
|
||||
'x-api-key': API_KEY,
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch application IDs');
|
||||
const fetchData = () => {
|
||||
setIsLoading(true);
|
||||
|
||||
fetch(`${BASE_URL}/application/list`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'accept': 'application/json',
|
||||
'x-api-key': API_KEY,
|
||||
},
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(appData => {
|
||||
if (appData.status_code === 200) {
|
||||
setApplicationIds(appData.details.data);
|
||||
return fetch(`${BASE_URL}/template/list?type=1`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'accept': 'application/json',
|
||||
'x-api-key': API_KEY,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
console.log('Response Data:', data);
|
||||
|
||||
if (data.status_code === 200) {
|
||||
setApplicationIds(data.details.data);
|
||||
setIsServer(false);
|
||||
setErrorMessage('Failed to fetch application IDs');
|
||||
throw new Error('Failed to fetch application IDs');
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(templateData => {
|
||||
if (templateData.status_code === 200) {
|
||||
const templates = templateData.details.data.map(template => ({
|
||||
value: template.id,
|
||||
label: template.name
|
||||
}));
|
||||
setTemplateOptions(templates);
|
||||
setIsServer(true);
|
||||
} else {
|
||||
setIsServer(false);
|
||||
throw new Error(data.details.message || 'Failed to fetch application IDs');
|
||||
setErrorMessage('Failed to fetch templates');
|
||||
throw new Error('Failed to fetch templates');
|
||||
}
|
||||
} catch (error) {
|
||||
setErrors(error.message || 'Error fetching application IDs');
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
setIsServer(false);
|
||||
} finally {
|
||||
setErrorMessage(error.message || 'Server connection failed');
|
||||
})
|
||||
.finally(() => {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchApplicationIds();
|
||||
});
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
const handleFocus = () => {
|
||||
setIsSelectOpen(true);
|
||||
};
|
||||
|
||||
const handleBlur = () => {
|
||||
setIsSelectOpen(false);
|
||||
};
|
||||
|
||||
|
||||
// Server Down Component
|
||||
if (!isServer) {
|
||||
return (
|
||||
<div style={{ textAlign: 'center', marginTop: '50px' }}>
|
||||
<img
|
||||
src={ServerDownAnimation}
|
||||
alt="Server Down Animation"
|
||||
style={{ width: '18rem', height: '18rem', marginBottom: '20px' }}
|
||||
/>
|
||||
<h2 style={{ color: 'red' }}>Server tidak dapat diakses</h2>
|
||||
<p>{errorMessage || 'Silakan periksa koneksi internet Anda atau coba lagi nanti.'}</p>
|
||||
<button
|
||||
onClick={() => window.location.reload()}
|
||||
style={{
|
||||
padding: '10px 20px',
|
||||
backgroundColor: '#0542cc',
|
||||
color: '#fff',
|
||||
border: 'none',
|
||||
borderRadius: '5px',
|
||||
cursor: 'pointer'
|
||||
}}>
|
||||
Coba Lagi
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const handleVerify = () => {
|
||||
setShowPreview(true); // Show the preview when verified
|
||||
};
|
||||
@ -239,18 +301,55 @@ const Auth = () => {
|
||||
|
||||
const handleClick = async () => {
|
||||
if (validate()) {
|
||||
// Log the input data
|
||||
console.log('Application ID:', applicationId);
|
||||
console.log('Expiry ID:', expiryId);
|
||||
console.log('OTP Length:', otpId);
|
||||
console.log('Phone Number:', '08' + phoneId); // Prepend "08" to the phone number
|
||||
console.log('Template ID:', templateId);
|
||||
|
||||
console.log('Make Auth Demo');
|
||||
setShowVerify(true);
|
||||
setIsLoading(true);
|
||||
|
||||
const requestData = {
|
||||
application_id: parseInt(applicationId),
|
||||
phone: phoneId,
|
||||
digits: parseInt(otpId),
|
||||
interval: parseInt(expiryId),
|
||||
template_id: parseInt(templateId),
|
||||
is_test: true,
|
||||
mode_id: 9
|
||||
};
|
||||
|
||||
console.log('Request Data:', requestData);
|
||||
|
||||
// Add timeout to ensure channel stays open
|
||||
const timeoutPromise = new Promise((_, reject) =>
|
||||
setTimeout(() => reject(new Error('Request timeout')), 30000)
|
||||
);
|
||||
|
||||
Promise.race([
|
||||
fetch(`${BASE_URL}/wa/otp`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-api-key': API_KEY
|
||||
},
|
||||
body: JSON.stringify(requestData)
|
||||
}),
|
||||
timeoutPromise
|
||||
])
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
console.log('API Response:', data);
|
||||
if (data.status_code === 201 && data.details.message === "Successfully") {
|
||||
setgenerateId(data.details.data.id);
|
||||
setShowVerify(false);
|
||||
alert('Authentication request successful!');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Request failed:', error);
|
||||
})
|
||||
.finally(() => {
|
||||
setIsLoading(false);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<div style={styles.section}>
|
||||
@ -299,62 +398,61 @@ const Auth = () => {
|
||||
|
||||
{/* Expiry and OTP */}
|
||||
<div className="form-group row align-items-center">
|
||||
{/* Expiry ID/Interval */}
|
||||
<div className="col-md-6">
|
||||
<div style={styles.selectWrapper}>
|
||||
<input
|
||||
type="number"
|
||||
id="expiryId"
|
||||
className="form-control"
|
||||
style={styles.select}
|
||||
value={expiryId}
|
||||
value={expiryId === 0 ? '' : expiryId}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value;
|
||||
// Check if the value is empty or a valid number
|
||||
if (value === '' || /^[0-9]+$/.test(value)) {
|
||||
setExpiryId(value === '' ? '' : parseInt(value, 10)); // Update state as number
|
||||
if (/^[0-9]+$/.test(value)) {
|
||||
setExpiryId(parseInt(value, 10));
|
||||
} else if (value === '') {
|
||||
setExpiryId(0);
|
||||
}
|
||||
}}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
placeholder="Expiry Time"
|
||||
/>
|
||||
{errors.expiryId && <small className="text-danger">{errors.expiryId}</small>}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{/* Digits */}
|
||||
<div className="col-md-6">
|
||||
<input
|
||||
type="text"
|
||||
<select
|
||||
id="otpId"
|
||||
className="form-control"
|
||||
placeholder="OTP Length"
|
||||
value={otpId}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value;
|
||||
// Allow only digits and enforce length constraints
|
||||
if (/^\d{0,6}$/.test(value)) {
|
||||
setOtpId(value);
|
||||
}
|
||||
}}
|
||||
minLength={6} // Enforce minimum length (for validation)
|
||||
maxLength={6} // Enforce maximum length
|
||||
/>
|
||||
onChange={(e) => setOtpId(e.target.value)}
|
||||
>
|
||||
<option value="">Select OTP Length</option>
|
||||
{[1, 2, 3, 4, 5, 6, 7, 8].map(length => (
|
||||
<option key={length} value={length}>
|
||||
{length}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
{errors.otpId && <small className="text-danger">{errors.otpId}</small>}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{/* Message and Phone */}
|
||||
<div className="form-group row align-items-center mt-4">
|
||||
<div className="col-md-6">
|
||||
<div style={styles.selectWrapper}>
|
||||
<input
|
||||
type="text"
|
||||
<Select
|
||||
id="templateId"
|
||||
className="form-control"
|
||||
placeholder="Template Name"
|
||||
value={templateId}
|
||||
onChange={(e) => setTemplateId(e.target.value)}
|
||||
value={templateOptions.find(option => option.value === templateId)}
|
||||
onChange={handleTemplateChange}
|
||||
options={templateOptions}
|
||||
placeholder="Select Template"
|
||||
isSearchable
|
||||
menuPortalTarget={document.body}
|
||||
menuPlacement="auto"
|
||||
/>
|
||||
{errors.templateId && <small className="text-danger">{errors.templateId}</small>}
|
||||
</div>
|
||||
@ -363,7 +461,7 @@ const Auth = () => {
|
||||
<div className="col-md-6">
|
||||
<div className="input-group">
|
||||
<span className="input-group-prepend">
|
||||
<span className="input-group-text">+62</span>
|
||||
<span className="input-group-text">Phone Number</span>
|
||||
</span>
|
||||
<input
|
||||
type="text"
|
||||
@ -395,8 +493,8 @@ const Auth = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{showVerify && <Verify phoneId={phoneId} onVerify={handleVerify} />}
|
||||
{showPreview && <Preview />}
|
||||
{showVerify && <Verify phoneId={phoneId} onVerify={handleVerify} generateId={generateId} />}
|
||||
<Preview otpLength={otpId} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -408,9 +506,6 @@ const styles = {
|
||||
section: {
|
||||
margin: '1rem 0 1rem 0'
|
||||
},
|
||||
formGroup: {
|
||||
marginTop: '-45px',
|
||||
},
|
||||
selectWrapper: {
|
||||
position: 'relative',
|
||||
marginTop: '0',
|
||||
@ -423,13 +518,6 @@ const styles = {
|
||||
border: 'none',
|
||||
outline: 'none',
|
||||
},
|
||||
chevronIcon: {
|
||||
position: 'absolute',
|
||||
right: '10px',
|
||||
top: '50%',
|
||||
transform: 'translateY(-50%)',
|
||||
pointerEvents: 'none',
|
||||
},
|
||||
remainingQuota: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
@ -446,17 +534,6 @@ const styles = {
|
||||
verticalAlign: 'super',
|
||||
fontSize: '20px',
|
||||
},
|
||||
buttonContainer: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center', // Center vertically within container
|
||||
position: 'absolute',
|
||||
right: '10px',
|
||||
top: '0',
|
||||
bottom: '0',
|
||||
height: '47px',
|
||||
margin: 'auto 0',
|
||||
},
|
||||
button: {
|
||||
background: 'none',
|
||||
border: 'none',
|
||||
@ -475,41 +552,6 @@ const styles = {
|
||||
position: 'relative',
|
||||
zIndex: 1,
|
||||
},
|
||||
uploadError: {
|
||||
color: 'red',
|
||||
fontSize: '12px',
|
||||
marginTop: '5px',
|
||||
},
|
||||
|
||||
containerResultStyle: {
|
||||
padding: '20px',
|
||||
border: '1px solid #0053b3',
|
||||
borderRadius: '5px',
|
||||
width: '100%',
|
||||
margin: '20px auto',
|
||||
},
|
||||
resultContainer: {
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
},
|
||||
tableStyle: {
|
||||
width: '60%',
|
||||
borderCollapse: 'collapse',
|
||||
},
|
||||
imageContainer: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'flex-start',
|
||||
width: '30%',
|
||||
marginLeft: '10px'
|
||||
},
|
||||
imageStyle: {
|
||||
width: '193px',
|
||||
height: '242px',
|
||||
borderRadius: '5px',
|
||||
},
|
||||
loadingOverlay: {
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
|
Loading…
x
Reference in New Issue
Block a user