From 2f97160893feb4129b8f3977beb039e42e3395c3 Mon Sep 17 00:00:00 2001 From: Rizqika Date: Tue, 24 Dec 2024 22:19:18 +0700 Subject: [PATCH] OTP-WA --- .../FaceRecognition/Section/Compare.jsx | 34 +- .../FaceRecognition/Section/Enroll.jsx | 10 + .../FaceRecognition/Section/Search.jsx | 18 +- .../FaceRecognition/Section/Verify.jsx | 11 + src/screens/Wa/Verify/Section/Auth.jsx | 416 ++++++++++-------- 5 files changed, 298 insertions(+), 191 deletions(-) diff --git a/src/screens/Biometric/FaceRecognition/Section/Compare.jsx b/src/screens/Biometric/FaceRecognition/Section/Compare.jsx index 8509a2e..0a3e9f6 100644 --- a/src/screens/Biometric/FaceRecognition/Section/Compare.jsx +++ b/src/screens/Biometric/FaceRecognition/Section/Compare.jsx @@ -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 && (
+ Selected preview

File: {selectedImageName}

{file && (

@@ -583,6 +604,17 @@ const Compare = () => { {/* Display uploaded image name */} {selectedCompareImageName && (

+ Compare preview

File: {selectedCompareImageName}

{compareFile && (

diff --git a/src/screens/Biometric/FaceRecognition/Section/Enroll.jsx b/src/screens/Biometric/FaceRecognition/Section/Enroll.jsx index daff7c7..4ec16ac 100644 --- a/src/screens/Biometric/FaceRecognition/Section/Enroll.jsx +++ b/src/screens/Biometric/FaceRecognition/Section/Enroll.jsx @@ -606,6 +606,16 @@ const Enroll = () => { {selectedImageName && (

+ Contoh Foto

File: {selectedImageName}

- -
-
- - +62 - - -
-
{/* 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 && {error}}
@@ -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 = () => {
- setInputValue(e.target.value)} - readOnly - /> + setInputValue(e.target.value)} + readOnly + />
- +
- ) -} + ); +}; + 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 ( +
+ Server Down Animation +

Server tidak dapat diakses

+

{errorMessage || 'Silakan periksa koneksi internet Anda atau coba lagi nanti.'}

+ +
+ ); + } 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 ( <>
@@ -299,62 +398,61 @@ const Auth = () => { {/* Expiry and OTP */}
+ {/* Expiry ID/Interval */}
{ 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 && {errors.expiryId}}
-
+ {/* Digits */}
- { - 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)} + > + + {[1, 2, 3, 4, 5, 6, 7, 8].map(length => ( + + ))} + {errors.otpId && {errors.otpId}}
+
{/* Message and Phone */}
- 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 && {errors.templateId}}
@@ -363,7 +461,7 @@ const Auth = () => {
- +62 + Phone Number {
- {showVerify && } - {showPreview && } + {showVerify && } + ); } @@ -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,