done recaptcha and solusi bisnis

This commit is contained in:
Carls2320 2025-02-14 02:37:44 +07:00
parent ff016f6f21
commit d4749582c1
9 changed files with 128 additions and 52 deletions

View File

@ -15,6 +15,9 @@
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
<script src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit"
async defer>
</script>
</body>
</html>

46
package-lock.json generated
View File

@ -10,6 +10,8 @@
"dependencies": {
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-google-recaptcha": "^3.1.0",
"react-recaptcha": "^2.3.10",
"react-router-dom": "^7.1.3"
},
"devDependencies": {
@ -3182,6 +3184,15 @@
"node": ">= 0.4"
}
},
"node_modules/hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
"license": "BSD-3-Clause",
"dependencies": {
"react-is": "^16.7.0"
}
},
"node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@ -3996,7 +4007,6 @@
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@ -4483,7 +4493,6 @@
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"dev": true,
"license": "MIT",
"dependencies": {
"loose-envify": "^1.4.0",
@ -4534,6 +4543,19 @@
"node": ">=0.10.0"
}
},
"node_modules/react-async-script": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/react-async-script/-/react-async-script-1.2.0.tgz",
"integrity": "sha512-bCpkbm9JiAuMGhkqoAiC0lLkb40DJ0HOEJIku+9JDjxX3Rcs+ztEOG13wbrOskt3n2DTrjshhaQ/iay+SnGg5Q==",
"license": "MIT",
"dependencies": {
"hoist-non-react-statics": "^3.3.0",
"prop-types": "^15.5.0"
},
"peerDependencies": {
"react": ">=16.4.1"
}
},
"node_modules/react-dom": {
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
@ -4547,13 +4569,31 @@
"react": "^18.3.1"
}
},
"node_modules/react-google-recaptcha": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/react-google-recaptcha/-/react-google-recaptcha-3.1.0.tgz",
"integrity": "sha512-cYW2/DWas8nEKZGD7SCu9BSuVz8iOcOLHChHyi7upUuVhkpkhYG/6N3KDiTQ3XAiZ2UAZkfvYKMfAHOzBOcGEg==",
"license": "MIT",
"dependencies": {
"prop-types": "^15.5.0",
"react-async-script": "^1.2.0"
},
"peerDependencies": {
"react": ">=16.4.1"
}
},
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"dev": true,
"license": "MIT"
},
"node_modules/react-recaptcha": {
"version": "2.3.10",
"resolved": "https://registry.npmjs.org/react-recaptcha/-/react-recaptcha-2.3.10.tgz",
"integrity": "sha512-IyanbozsYCuHvTYDuskZTIEcRAMG/sdvAu5b29iQWoC8Kd3Zk9WGCv2oNxh6RfGHvSvgHAyaLjmC6ei/yMsJ7g==",
"license": "BSD"
},
"node_modules/react-refresh": {
"version": "0.14.2",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",

View File

@ -12,6 +12,8 @@
"dependencies": {
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-google-recaptcha": "^3.1.0",
"react-recaptcha": "^2.3.10",
"react-router-dom": "^7.1.3"
},
"devDependencies": {

View File

@ -1,11 +1,28 @@
import { img10 } from "./asset";
import { useState } from "react";
import { img10 } from "./asset"; // Pastikan img10 ada dan benar path-nya
import ReCAPTCHA from "react-google-recaptcha"; // Pastikan penulisan benar (ReCAPTCHA)
const ContactForm = () => {
// State untuk melacak status reCAPTCHA
const [isCaptchaVerified, setIsCaptchaVerified] = useState(false);
// Callback yang dijalankan setelah reCAPTCHA dimuat
const callback = function () {
console.log("Done!!!!");
};
// Callback verifikasi reCAPTCHA
const verifyCallback = function (response) {
if (response) {
setIsCaptchaVerified(true); // Jika reCAPTCHA berhasil, set state menjadi true
}
};
return (
<div
className="flex min-h-screen items-center justify-center p-5 text-white rounded-tl-[80px] rounded-tr-[80px]"
style={{
background: `linear-gradient(45deg, #DC016866 15%, rgba(91, 89, 232, 1),rgba(91, 89, 232, 1)), url(${img10})`,
background: `linear-gradient(45deg, #DC016866 15%, rgba(91, 89, 232, 1), rgba(91, 89, 232, 1)), url(${img10})`,
backgroundSize: "cover",
backgroundPosition: "center",
backgroundRepeat: "no-repeat",
@ -14,38 +31,24 @@ const ContactForm = () => {
<div className="flex flex-col lg:flex-row items-start w-full 2xl:max-w-[100%] 2xl:max-h-[100%] sm:mt-[79px] sm:mb-[79px] 2xl:mt-[-125px] 2xl:px-10">
{/* Bagian Kiri */}
<div className="flex-1 p-5 mb-10 lg:mb-0 md:ml-28 2xl:mt-28 md:max-w-[40%]">
{/* Judul utama */}
<h2 className="text-[32px] font-[550] leading-[48px] tracking-[0.005em] text-left mt-16 mb-[39px] 2xl:text-[36px] 2xl:leading-[52px]">
Isi formulir di samping ini dan kami akan menghubungi Anda secepat
mungkin
Isi formulir di samping ini dan kami akan menghubungi Anda secepat mungkin
</h2>
{/* Deskripsi tambahan */}
<p className="text-[18px] font-[400] leading-[30px] tracking-[0.005em] text-left mb-6 2xl:text-[20px] 2xl:leading-[34px]">
Kami percaya bahwa kolaborasi adalah kunci kesuksesan. Hubungi kami
hari ini, dan mari wujudkan masa depan bisnis Anda bersama Rekan AI!
Kami percaya bahwa kolaborasi adalah kunci kesuksesan. Hubungi kami hari ini, dan mari wujudkan masa depan bisnis Anda bersama Rekan AI!
</p>
</div>
{/* Bagian Form */}
<div
className="bg-white text-[#212121] mt-5 rounded-[25px] md:ml-24 md:mt-52 p-[40px] border-[8px] border-[#F086A4] md:w-[794px] 2xl:px-[60px] 2xl:py-[50px]"
>
{/* Judul form */}
<div className="bg-white text-[#212121] mt-5 rounded-[25px] md:ml-24 md:mt-52 p-[40px] border-[8px] border-[#F086A4] md:w-[794px] 2xl:px-[60px] 2xl:py-[50px]">
<h3 className="text-[32px] font-[550] mb-4 text-[#212121] 2xl:text-[28px] 2xl:mb-6">
Hubungi Tim Terbaik Kami
</h3>
{/* Deskripsi form */}
<p className="mb-6 text-[#212121] 2xl:text-[18px]">
Silakan isi data diri Anda pada formulir di bawah ini
</p>
{/* Formulir input */}
<form className="flex flex-col gap-4 px-2">
{/* Input Nama */}
<div>
<label className="text-[#6B5CEA]">Nama (Tanpa Gelar)*</label>
<input
@ -55,8 +58,6 @@ const ContactForm = () => {
className="w-full mt-1 p-4 border border-[#5B59E8] rounded-lg focus:ring-2 focus:ring-[#5B59E8] 2xl:p-5"
/>
</div>
{/* Input Nomor Telepon */}
<div>
<label className="text-[#6B5CEA]">Nomor Telepon*</label>
<input
@ -70,8 +71,6 @@ const ContactForm = () => {
}}
/>
</div>
{/* Input Email */}
<div>
<label className="text-[#6B5CEA]">Email*</label>
<input
@ -81,20 +80,14 @@ const ContactForm = () => {
className="w-full mt-1 p-4 border border-[#5B59E8] rounded-lg focus:ring-2 focus:ring-[#5B59E8] 2xl:p-5"
/>
</div>
{/* Input Nama Organisasi */}
<div>
<label className="text-[#6B5CEA]">
Nama Organisasi/Perusahaan
</label>
<label className="text-[#6B5CEA]">Nama Organisasi/Perusahaan</label>
<input
type="text"
placeholder="Masukkan nama organisasi/perusahaan Anda"
className="w-full mt-1 p-4 border border-[#5B59E8] rounded-lg focus:ring-2 focus:ring-[#5B59E8] 2xl:p-5"
/>
</div>
{/* Dropdown Pilih Kebutuhan */}
<div className="relative">
<label className="text-[#6B5CEA]">Pilih Kebutuhan</label>
<select
@ -106,10 +99,9 @@ const ContactForm = () => {
<option value="pelatihan">Pelatihan</option>
<option value="pengembangan">Pengembangan</option>
</select>
{/* Tanda panah kustom */}
<div className="absolute inset-y-0 right-5 flex items-center pointer-events-none">
<svg
className="w-6 text-[#5B59E8]"
className="w-8 h-28 mt-7 text-[#5B59E8]"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
@ -125,20 +117,19 @@ const ContactForm = () => {
</div>
</div>
{/* Checkbox Verifikasi */}
{/* ReCAPTCHA */}
<div className="flex items-center gap-2">
<input
type="checkbox"
required
className="focus:ring-2 focus:ring-indigo-500"
<ReCAPTCHA
sitekey="6LcIYNYqAAAAAJW_YJRWbBDBGt1gsKwsL9vK1SfW"
onChange={verifyCallback} // Mengganti render="explicit" dengan onChange yang lebih sederhana
onLoad={callback} // Callback ketika ReCAPTCHA sudah dimuat
/>
<span className="text-[#212121]">Saya bukan robot</span>
</div>
{/* Tombol Kirim */}
<button
type="submit"
className="bg-indigo-500 hover:bg-indigo-600 text-white py-3 px-6 rounded-lg shadow-md transition-all w-[173px] 2xl:w-[200px] 2xl:py-4"
disabled={!isCaptchaVerified} // Tombol dinonaktifkan jika reCAPTCHA belum dicentang
>
Kirim Pesan
</button>

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 B

View File

@ -18,8 +18,9 @@ import telephone from './telephone.png';
import wagreen from './whatsapp-color_svgrepo.com.png';
import wabutton from './Whatsapp22.png';
import x from './x.png';
import arrowform from './Dribbble-Light-Preview.png';
export{
row,component11,customer,email,facebook,frame,frame27,img10,instagram,kmn,kominfo,Logo1,nvidia,ojk,phone,search,telephone,wagreen,wabutton,x
row,component11,customer,email,facebook,frame,frame27,img10,instagram,kmn,kominfo,Logo1,nvidia,ojk,phone,search,telephone,wagreen,wabutton,x,arrowform
}

View File

@ -1,9 +1,22 @@
import { useState } from "react";
import { useState, useEffect } from "react";
import { data } from "./data";
export default function BusinessSolution() {
const [activeTab, setActiveTab] = useState("hr");
const activeContent = data.content[activeTab];
const [isMobile, setIsMobile] = useState(false);
// Detect mobile screen size
useEffect(() => {
const handleResize = () => {
setIsMobile(window.innerWidth < 768); // Set to true if screen width is less than 768px
};
window.addEventListener("resize", handleResize);
handleResize(); // Initialize state on component mount
return () => window.removeEventListener("resize", handleResize); // Cleanup on unmount
}, []);
return (
<div className="min-h-screen p-4">
@ -15,7 +28,7 @@ export default function BusinessSolution() {
{/* Navigation Buttons */}
<div className="relative w-full max-w-[1539px] h-auto mx-auto bg-[#CA2B68] rounded-[14px] border border-[#D5DAE3] mb-8 p-4">
<div className="flex flex-wrap justify-center gap-4 text-[18px] md:text-[24px]">
<div className={`flex ${isMobile ? 'flex-wrap' : 'justify-center'} gap-4 text-[18px] md:text-[24px]`}>
{data.tabs.map((button) => (
<button
key={button.key}
@ -34,7 +47,7 @@ export default function BusinessSolution() {
{activeContent && (
<div className="max-w-7xl mx-auto flex flex-col sm:flex-row flex-wrap gap-8 justify-center lg:justify-start">
{/* Image Section */}
<div className="w-full sm:w-[400px] md:w-[500px] lg:w-[618px] h-auto rounded-[14px] border border-gray-200 overflow-hidden shadow-lg">
<div className={`w-full ${isMobile ? "sm:w-[300px]" : "sm:w-[400px]"} md:w-[500px] lg:w-[618px] h-auto rounded-[14px] border border-gray-200 overflow-hidden shadow-lg`}>
<img
src={activeContent.image || "/placeholder.svg"}
alt={activeContent.title}

View File

@ -1,9 +1,22 @@
import { useState } from "react";
import { useState, useEffect } from "react";
import { data } from "./data";
export default function BusinessSolution() {
const [activeTab, setActiveTab] = useState("hr");
const activeContent = data.content[activeTab];
const [isMobile, setIsMobile] = useState(false);
// Detect mobile screen size
useEffect(() => {
const handleResize = () => {
setIsMobile(window.innerWidth < 768); // Set to true if screen width is less than 768px
};
window.addEventListener("resize", handleResize);
handleResize(); // Initialize state on component mount
return () => window.removeEventListener("resize", handleResize); // Cleanup on unmount
}, []);
return (
<div className="min-h-screen p-4">
@ -15,7 +28,7 @@ export default function BusinessSolution() {
{/* Navigation Buttons */}
<div className="relative w-full max-w-[1539px] h-auto mx-auto bg-[#CA2B68] rounded-[14px] border border-[#D5DAE3] mb-8 p-4">
<div className="flex flex-wrap justify-center gap-4 text-[18px] md:text-[24px]">
<div className={`flex ${isMobile ? 'flex-wrap' : 'justify-center'} gap-4 text-[18px] md:text-[24px]`}>
{data.tabs.map((button) => (
<button
key={button.key}
@ -34,7 +47,7 @@ export default function BusinessSolution() {
{activeContent && (
<div className="max-w-7xl mx-auto flex flex-col sm:flex-row flex-wrap gap-8 justify-center lg:justify-start">
{/* Image Section */}
<div className="w-full sm:w-[400px] md:w-[500px] lg:w-[618px] h-auto rounded-[14px] border border-gray-200 overflow-hidden shadow-lg">
<div className={`w-full ${isMobile ? "sm:w-[300px]" : "sm:w-[400px]"} md:w-[500px] lg:w-[618px] h-auto rounded-[14px] border border-gray-200 overflow-hidden shadow-lg`}>
<img
src={activeContent.image || "/placeholder.svg"}
alt={activeContent.title}

View File

@ -1,9 +1,22 @@
import { useState } from "react";
import { useState, useEffect } from "react";
import { data } from "./data";
export default function BusinessSolution() {
const [activeTab, setActiveTab] = useState("hr");
const activeContent = data.content[activeTab];
const [isMobile, setIsMobile] = useState(false);
// Detect mobile screen size
useEffect(() => {
const handleResize = () => {
setIsMobile(window.innerWidth < 768); // Set to true if screen width is less than 768px
};
window.addEventListener("resize", handleResize);
handleResize(); // Initialize state on component mount
return () => window.removeEventListener("resize", handleResize); // Cleanup on unmount
}, []);
return (
<div className="min-h-screen p-4">
@ -15,7 +28,7 @@ export default function BusinessSolution() {
{/* Navigation Buttons */}
<div className="relative w-full max-w-[1539px] h-auto mx-auto bg-[#CA2B68] rounded-[14px] border border-[#D5DAE3] mb-8 p-4">
<div className="flex flex-wrap justify-center gap-4 text-[18px] md:text-[24px]">
<div className={`flex ${isMobile ? 'flex-wrap' : 'justify-center'} gap-4 text-[18px] md:text-[24px]`}>
{data.tabs.map((button) => (
<button
key={button.key}
@ -34,7 +47,7 @@ export default function BusinessSolution() {
{activeContent && (
<div className="max-w-7xl mx-auto flex flex-col sm:flex-row flex-wrap gap-8 justify-center lg:justify-start">
{/* Image Section */}
<div className="w-full sm:w-[400px] md:w-[500px] lg:w-[618px] h-auto rounded-[14px] border border-gray-200 overflow-hidden shadow-lg">
<div className={`w-full ${isMobile ? "sm:w-[300px]" : "sm:w-[400px]"} md:w-[500px] lg:w-[618px] h-auto rounded-[14px] border border-gray-200 overflow-hidden shadow-lg`}>
<img
src={activeContent.image || "/placeholder.svg"}
alt={activeContent.title}