1<!DOCTYPE html><html lang="en" amp> 2<head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# website: http://ogp.me/ns/website# fb: profile: http://ogp.me/ns/profile# fb: article: http://ogp.me/ns/article#"> 3 <meta charset="UTF-8" /> 4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 5 <title>Haroct</title> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=2"> 6<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <link rel="manifest" href="/themes/store/default/assets/manifest.json" /> 7 <link rel="preconnect" href="https://fonts.gstatic.com"> 8 9 <meta name="theme-color" content="#f55858"/> 10 <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous"> 11 <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/build/css/intlTelInput.css"> 12 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css" integrity="sha512-SnH5WK+bZxgPHs44uWIX+LLJAJ9/2PkPKZ5QiAj6Ta86w+fsb2TkcmfRyVX3pBnMFcV7oQPJkl9QevSCWr3W6A==" crossorigin="anonymous" referrerpolicy="no-referrer" /> 13 <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/select2.min.css" rel="stylesheet"> 14 <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/select2-bootstrap-5-theme.min.css" rel="stylesheet"> 15 <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/driver.js@latest/dist/driver.css"> 16 17 <link href="/themes/store/default/assets/css/fonts.css" media="screen" rel="stylesheet" type="text/css"> 18<link href="/themes/store/default/assets/css/libs.css" media="screen" rel="stylesheet" type="text/css"> 19<link href="/themes/store/default/assets/css/styles.css" media="screen" rel="stylesheet" type="text/css"> 20<link href="/themes/store/default/assets/js/notifyjs/metro/notify-metro.css" media="screen" rel="stylesheet" type="text/css"> 21<link href="/themes/store/default/assets/css/custom.css?v=1790258645" media="screen" rel="stylesheet" type="text/css"> 22<link href="/themes/store/default/assets/css/dashboard.css?v=1790258645" media="screen" rel="stylesheet" type="text/css"> 23<link href="/themes/store/default/assets/favicon/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon"> <link href="/themes/store/default/assets/css/fonts.css" as="style" rel="preload"> 24 <!-- icon in the highest resolution we need it for --> 25 <link rel="icon" sizes="192x192" href="/themes/store/default/assets/favicon/android-icon-192x192.png"> 26 27 <!-- reuse same icon for Safari --> 28 <link rel="apple-touch-icon" href="/themes/store/default/assets/favicon/apple-icon.png"> 29 <!-- meta tags --> 30 <meta itemprop="url" content="https://haroct.com.my" /> 31 <meta name="HandheldFriendly" content="true" /> 32 <meta name="application-name" content="haroct.com.my" /> 33 <meta name="apple-touch-fullscreen" content="yes" /> 34 <meta property="article:publisher" content="https://facebook.com"/> 35 <meta property="og:locale" itemprop="inLanguage" content="en_GB"/> 36 <meta property="og:locale:alternate" itemprop="inLanguage" content="en_GB" /> 37 <!--<meta property="og:site_name" content=""/>--> 38 <!--<meta property="fb:admins" content="100003215492421">--> 39 <meta name="author" content="Haroct" /> 40 <meta name="Robots" content="index, follow" /> 41 <meta name="format-detection" content="telephone=no" /> 42 <link rel="canonical" href="https://haroct.com.my/store/signup" /> <!-- fixed duplicated meta data --> 43 <meta content='#f55858' name='msapplication-TileColor' /> 44 45 <link rel='dns-prefetch' href='//maps.google.com' />
46 <link rel='dns-prefetch' href='//www.gstatic.com' /> 47 <link rel='dns-prefetch' href='//fonts.googleapis.com' /> 48
48<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
48 49
49<script src="/themes/store/default/assets/js/jquery/jquery-3.1.0.min.js"></script>
49 50
50<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/select2.min.js"></script>
50 51
51<script src="/themes/store/default/assets/js/notifyjs/notify.min.js"></script>
51 52
52<script src="/themes/store/default/assets/js/notifyjs/metro/notify-metro.js"></script>
52 53
53<script src="https://cdn.jsdelivr.net/npm/driver.js@latest/dist/driver.js.iife.js"></script>
53 54
54<script src="/themes/store/default/assets/js/custom.js"></script>
vendor: 68 bytes, lines 54-55
54 55 <script async src="https://www.googletagmanager.com/gtag/js?id=
55G-LG2RC922TR
vendor: 16 bytes, lines 55-56
55"></script> 56
56<script> 57
vendor: 152 bytes, lines 57-61
57window.dataLayer = window.dataLayer || []; 58 function gtag(){dataLayer.push(arguments);} 59 gtag('js', new Date()); 60 61 gtag('config', '
61G-LG2RC922TR
vendor: 8 bytes, lines 61-62
61'); 62
62</script>
62 63</head> 64 <body> 65 <div class=""> 66 <div class="container-fluid min-vh-100"> 67 <div class="row"> 68 <div class="col-md-3 bg-haroct bg-btm text-light d-flex align-items-center justify-content-center p-5 p-md-0"> 69 <div class="mb-5 pb-5 text-center"> 70 <img src="/themes/store/default/assets/images/logo-haroct.png" class="signup-logo"> 71 <h4 class="mt-3">Welcome To Haroct</h4> 72 <div class="mt-2"> 73 <ul class="navbar-nav ms-auto"> 74 <li class="nav-item dropdown"> 75 <a class="nav-link px-3 dropdown-toggle text-light" id="langDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false" href="#"> 76 <img src="/themes/store/default/assets/images/icon-globe.png" class="mr-1 bg-haroct rounded-5" style="width: 18px; height: 18px;"> EN </a> 77 <ul class="dropdown-menu" aria-labelledby="langDropdown"> 78 <li><a class="dropdown-item" href="https://haroct.com.my/store/signup?lang=en">EN</a></li> 79 <li><a class="dropdown-item" href="https://haroct.com.my/store/signup?lang=my">MY</a></li> 80 <li><a class="dropdown-item" href="https://haroct.com.my/store/signup?lang=cn">䏿</a></li> 81 <li><a class="dropdown-item" href="https://haroct.com.my/store/signup?lang=th">à¹à¸à¸¢</a></li> 82 </ul> 83 </li> 84 </ul> 85 </div> 86 <div class="steps"> 87 <div class="step"> 88 <div class="step-number">01</div> 89 <div class="step-text text-start"> 90 <b>Verify your contact number</b> 91 <small>Enter the OTP number</small> 92 </div> 93 </div> 94 <div class="connector" id="line-1"></div> 95 <div class="step"> 96 <div class="step-number">
9602</div> 97 <div class="step-text text-start"> 98 <b>Your Role</b> 99 <small>Select your role and field</small> 100 </div> 101 </div> 102 <div class="connector" id="line-2"></div> 103 <div class="step"> 104 <div class="step-number">03</div> 105 <div class="step-text text-start"> 106 <b>Your Vertical</b> 107 <small>Select your business's vertical</small> 108 </div> 109 </div> 110 <div class="connector" id="line-3"></div> 111 <div class="step"> 112 <div class="step-number">04</div> 113 <div class="step-text text-start"> 114 <b>Your Business Details</b> 115 <small>Provide your company details</small> 116 </div> 117 </div> 118 </div> 119 </div> 120 </div> 121 <div class="col signup-right-col d-flex align-items-start justify-content-center text-center pt-3 pb-5 px-md-0"> 122 <div class="row justify-content-center w-100"> 123 <div class="col-md-12 signup-content-col"> 124 <aside class="main-content"> 125 <section> 126 <form id="signupForm"> 127 <input type="hidden" name="lang" value="en"> 128 129 <div id="invitation-banner" class="d-none mb-4"> 130 <div class="alert alert-info text-start mb-0"> 131 <p class="fw-bold mb-1">Welcome to Haroct!</p> 132 <p class="mb-0"><span id="inviter-name"></span> has invited you to join their company account as a sub-account.</p> 133 </div> 134 </div> 135 136 <div class="row justify-content-center"> 137 <div class="col-md-9"> 138 <div id="progress" class="text-center mb-3"> 139 <div class="row position-relative"> 140 <div class="position-absolute text-left mt-2 start-0 w-auto z-1"> 141 <a id="back-btn" class="btn"><i class="fa fa-arrow-left"></i></a> 142 </div> 143 <div class="col-12 center-text p-3"> 144 <span id="step-info">1 of 3</span> 145 </div> 146 </div> 147 148 <div class="progress progress-thin"> 149 <div class="progress-bar bg-warning w-33" role="progressbar" aria-valuenow="33" aria-valuemin="0" aria-valuemax="100"></div> 150 </div> 151 </div> 152 153 <div class="row justify-content-center text-center"> 154 <div class="col-md-8"> 155 <div id="step1" class="form-step active-step"> 156 <h4 class="text-center my-5">How can we contact you?</h4> 157 <div class="mb-3"> 158 <div class="row mb-3"> 159 <div class="col-12"> 160 <input type="tel" id="phone" name="phone" class="form-control required" placeholder="Phone Number"> 161 <input type="hidden" class="form-control" name="countryCode" value=""> 162 </div> 163 <div class="col-12 thai-guide text-left mt-2 d-none"> 164 <span class="default-thai-guide text-muted"> 165 à¸à¸£à¸¸à¸à¸²à¹à¸¥à¸·à¸à¸à¸£à¸«à¸±à¸ªà¸à¸£à¸°à¹à¸à¸¨ (+66) <span class="fw-bold">à¹à¸à¸¢à¹à¸¡à¹à¸à¸´à¸¡à¸à¹à¹à¸¥à¸ "0"</span> à¸à¸³à¸«à¸à¹à¸² à¸à¸²à¸¡à¸à¹à¸§à¸¢à¸«à¸¡à¸²à¸¢à¹à¸¥à¸à¹à¸à¸£à¸¨à¸±à¸à¸à¹à¸à¸à¸à¸à¹à¸²à¸ 166 </span> 167 <span class="error-thai-guide text-danger d-none"> 168 หมายà¹à¸¥à¸à¹à¸à¸£à¸¨à¸±à¸à¸à¹à¹à¸¡à¹à¸à¸¹à¸à¸à¹à¸à¸ à¸à¸£à¸¸à¸à¸²à¹à¸¥à¸·à¸à¸à¸£à¸«à¸±à¸ªà¸à¸£à¸°à¹à¸à¸¨ (+66) <span class="fw-bold">à¹à¸à¸¢à¹à¸¡à¹à¸à¸´à¸¡à¸à¹à¹à¸¥à¸ "0"</span> à¸à¸³à¸«à¸à¹à¸² à¸à¸²à¸¡à¸à¹à¸§à¸¢à¸«à¸¡à¸²à¸¢à¹à¸¥à¸à¹à¸à¸£à¸¨à¸±à¸à¸à¹à¸à¸à¸à¸à¹à¸²à¸ 169 </span> 170 </div> 171 </div> 172 173 <input type="hidden" name="otp" value=""> 174 <input type="hidden" name="code" value=""> 175 <input type="hidden" name="version" value="2"> 176 177 <div class="row mb-2 mt-5"> 178 <div class="col"> 179 <div class="form-check"> 180 <input class="form-check-input required" type="checkbox" name="tnc" value="1" id="tnc" required> 181 <label class="form-check-label" for="tnc"> 182 By ticking, you are confirming you have read, understood and agree to HAROCT Terms & Condition and End User License Agreement </label> 183 </div> 184 </div> 185 </div> 186 187 <div class="row mb-2 mt-4 bg-haroct-2 roun
187ded-1 mx-0 py-2"> 188 <div class="col align-self-center text-left"> 189 <i class="fa fa-gift link-haroct fs-6 mr-1"></i> Have a referral code? </div> 190 <div class="col-auto text-right"> 191 <input type="button" class="btn btn-sm bg-light border-haroct btn-claim" value="Claim" data-bs-toggle="modal" data-bs-target="#referralModal"> 192 </div> 193 <div class="col-12 text-left fw-bold referral-block d-none"> 194 Referral code: <span></span> 195 <a href="#" class="btn-remove-referral link-haroct text-decoration-underline ml-2">Remove</a> 196 </div> 197 </div> 198 199 <div class="row justify-content-center mb-3 mt-4"> 200 <button type="button" class="btn btn-haroct btn-request-otp mt-4 w-50" disabled>Request OTP</button> 201 </div> 202 203 <div class="row"> 204 <div class="col text-center"> 205 <div>Already have an account?</div> 206 <a href="login" class="link-haroct">Login</a> 207 </div> 208 </div> 209 </div> 210 </div> 211 212 <div id="step2" class="form-step"> 213 <h4 class="text-center my-5">Set your passcode</h4> 214 <div class="mb-3"> 215 <div class="pin-inputs mb-4"> 216 <input type="text" maxlength="1" class="pin" /> 217 <input type="text" maxlength="1" class="pin" /> 218 <input type="text" maxlength="1" class="pin" /> 219 <input type="text" maxlength="1" class="pin" /> 220 <input type="text" maxlength="1" class="pin" /> 221 <input type="text" maxlength="1" class="pin" /> 222 </div> 223 </div> 224 225 <button type="button" class="btn btn-haroct next-btn mt-4 w-50" disabled>Continue</button> 226 </div> 227 228 <div id="step3" class="form-step"> 229 <h4 class="text-center my-5">What is your role?</h4> 230 <div class="mb-3"> 231 <div class="row"> 232 <div class="col"> 233 <div class="radio-box role-box" data-role="seller"> 234 <input type="radio" class="required" id="role1" name="accountType" value="1"> 235 <label for="role1" class="p-1 p-md-3"> 236 <div class="mt-2">I am a seller</div> 237 </label> 238 </div> 239 </div> 240 <div class="col"> 241 <div class="radio-box role-box" data-role="buyer"> 242 <input type="radio" class="required" id="role2" name="accountType" value="0"> 243 <label for="role2" class="p-1 p-md-3"> 244 <div class="mt-2">I am a buyer</div> 245 </label> 246 </div> 247 </div> 248 </div> 249 250 <div class="row mt-2 bg-haroct-2 rounded-1 mx-0 py-2 block-role-info d-none"> 251 <span></span> 252 </div> 253 </div> 254 255 <!-- Buyer Fields --> 256 <div id="buyer-fields" class="d-none"> 257 <h4 class="text-center my-5">I am a...</h4> 258 <div class="mb-3"> 259 <div class="row"> 260 <!-- Buyer segments will be dynamically inserted here --> 261 </div> 262 </div> 263 </div> 264 265 <!-- Seller field --> 266 <div id="seller-fields" class="d-none"> 267 <h4 class="text-center my-5">I am a...</h4> 268 <div class="mb-3"> 269 <div class="row"> 270 <!-- Seller segments will be dynamically inserted here --> 271 </div> 272 </div> 273 </div> 274 275 <div class="row mt-2 bg-haroct-2 roun
275ded-1 mx-0 py-2 block-role-info d-none"> 276 <span></span> 277 </div> 278 <button type="button" class="btn btn-haroct next-btn mt-4 w-50" disabled>Continue</button> 279 </div> 280 281 <div id="stepMarket" class="form-step"> 282 <h4 class="text-center mt-5 mb-1">Where is your business based? <small class="text-muted text-uppercase">Required</small></h4> 283 <div class="mb-4"> 284 <div class="row justify-content-center" id="businessCountryOptions"> 285 <!-- Business country options rendered dynamically --> 286 </div> 287 </div> 288 289 <h4 class="text-center mt-5 mb-1" id="marketQuestionHeading">Which market do you want to buy from?</h4> 290 <p class="text-muted text-center mb-3">Select all that apply</p> 291 <div class="mb-3" id="interestedMarketOptions"> 292 <!-- Interested market checkboxes rendered dynamically --> 293 </div> 294 295 <button type="button" class="btn btn-haroct next-btn mt-4 w-50" disabled>Continue</button> 296 </div> 297 298 <div id="step4" class="form-step"> 299 <h4 class="text-center my-5">What do you sell?</h4> 300 <div class="mb-3 non-agri-product"> 301 <!-- Normal Options --> 302 <div class="row"> 303 <div class="col"> 304 <div class="radio-box"> 305 <input type="radio" class="required" id="vertical1" name="verticalType" value="vegetable"> 306 <label for="vertical1" class="p-1 p-md-3"> 307 <img src="/themes/store/default/assets/images/vege1.png"> 308 <div class="mt-2">I am selling vegetables</div> 309 </label> 310 </div> 311 </div> 312 <div class="col"> 313 <div class="radio-box"> 314 <input type="radio" class="required" id="vertical2" name="verticalType" value="fruit"> 315 <label for="vertical2" class="p-1 p-md-3"> 316 <img src="/themes/store/default/assets/images/fruit.png"> 317 <div class="mt-2">I am selling fruits</div> 318 </label> 319 </div> 320 </div> 321 </div> 322 </div> 323 324 <!-- Agri Input Product Options --> 325 <div class="mb-3 agri-input-product d-none"> 326 <h6 class="text-center my-5">You can choose more than 1 option</h6> 327 <div class="row agri-product-container" id="agriProductContainer"> 328 <!-- Products will be dynamically inserted here --> 329 </div> 330 </div> 331 <button type="button" class="btn btn-haroct next-btn mt-4 w-50" disabled>Continue</button> 332 </div> 333 334 <div id="step5" class="form-step"> 335 <!-- Normal Form --> 336 <div class="normal-form"> 337 <h4 class="text-center my-5">Tell us your business</h4> 338 <div class="row text-start mb-3"> 339 <div class="col-6"> 340 <div class="form-check"> 341 <input class="form-check-input" type="radio" name="isBusiness" id="radioBusiness" value="1" checked> 342 <label class="form-check-label" for="radioBusiness">Business</label> 343 </div> 344 </div> 345 <div class="col-6"> 346 <div class="form-check"> 347 <input class="form-check-input" type="radio" name="isBusiness" id="radioPersonal" value="
3470"> 348 <label class="form-check-label" for="radioPersonal">Personal</label> 349 </div> 350 </div> 351 </div> 352 <div class="mb-4"> 353 <input type="text" class="form-select " name="name" placeholder="Enter Your Business Name" required> 354 </div> 355 <div class="mb-4"> 356 <input type="text" class="form-select " name="email" placeholder="Enter Your Email Address"> 357 </div> 358 <div class="p-3 border border-dashed rounded upload-container d-flex align-items-center justify-content-between mb-4"> 359 <div class="d-flex align-items-center"> 360 <div id="logo-placeholder" class="logo-placeholder rounded-circle me-3"> 361 <svg class="placeholder-icon" fill="currentColor" viewBox="0 0 16 16"> 362 <path d="M11 6a3 3 0 1 1-6 0 3 3 0 0 1 6 0"/> 363 <path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8m8-7a7 7 0 0 0-5.468 11.37C3.242 11.226 4.805 10 8 10s4.758 1.226 5.468 2.37A7 7 0 0 0 8 1"/> 364 </svg> 365 </div> 366 <label for="storeLogoInput" class="mb-0">Tap to upload your store logo</label> 367 <input type="file" class="d-none" id="storeLogoInput" accept="image/*"> 368 </div> 369 370 <button type="button" class="btn btn-sm btn-primary upload-btn">Upload Photo</button> 371 </div> 372 <div class="mb-4"> 373 <select class="form-select " id="stateSelect" name="locationState" required> 374 <option value="" selected>Select State</option> 375 </select> 376 </div> 377 <div class="mb-5"> 378 <select class="form-select" name="locationCity" id="citySelect" required> 379 <option value="" selected>Select City</option> 380 </select> 381 </div> 382 <div class="text-center"> 383 <button type="submit" class="btn btn-haroct mt-4 w-50">Sign Up Now</button> 384 </div> 385 </div> 386 387 <!-- Agri Input Form --> 388 <div class="agri-input-form d-none"> 389 <h4 class="text-center my-5">Tell us about your business</h4> 390 <div class="mb-3"> 391 <ul class="nav nav-tabs d-flex justify-content-center w-100"> 392 <li class="nav-item"> 393 <a class="nav-link active" data-bs-toggle="tab" href="#business" data-is-business="true">Business</a> 394 </li> 395 <li class="nav-item"> 396 <a class="nav-link" data-bs-toggle="tab" href="#personal" data-is-business="false">Personal</a> 397 </li> 398 </ul> 399 <div class="tab-content mt-4"> 400 <div class="tab-pane fade show active" id="business"> 401 <form> 402 <div class="row"> 403 <div class="col-md-6 text-left"> 404 <label for="businessName" class="form-label text-muted label-step-4">Company Name <span class="text-danger">*</span></label> 405 <input type="text" class="form-control mb-3" id="businessName" name="store[name]"> 406 407 <label for="personInChargeName" class="form-label text-muted label-step-4">Person In Charge's Name <span class="text-danger">*</span></label> 408 <input type="text" class="form-control mb-3" id="personInChargeName" name="name"> 409 410 <div class="row mb-3"> 411 <div class="col"> 412 <label for="businessAddress" class="form-label text-muted label-step-4">Company Address (Optional)</label> 413 <div class="address-container border rounded p-2 d-flex justify-content-between align-items-center w-100"> 414 <div class="text-haroct me-3"> 415 <i class="fa fa-map-marker" aria-hidden="true"></i> 416 </div> 417 <span id="businessAddressDisplay" class="text-muted">No address added yet</span> 418 <button type="button" class="btn btn-sm btn-outline-secondary" data-bs-toggle="modal" data-bs-target="#addCompanyAddressModal"> 419 Add </button> 420 </div> 421 <input type="hidden" id="businessAddress" name="businessAddress"> 422 </div> 423 </div> 424 </div> 425 <div class="col-md-6 text-left"> 426 <label for="businessRegNo" class="form-label text-muted label-step-4">Business Registration Number (SSM Registration Number) (Optional)</label> 427 <input type="number" class="form-control mb-3" id="businessRegNo" name="store[ssmNumber]"> 428 429 <label for="personInChargeContact" class="form-label text-muted label-step-4"> 430 Person In Charge's Contact Number <sp
430an class="text-danger">*</span> 431 </label> 432 <div class="phone-input-container mb-3"> 433 <input type="tel" id="personInChargeContact" name="store[phone]" class="form-control" placeholder=""> 434 <input type="hidden" name="countryCode" value=""> 435 </div> 436 <label for="businessEmail" class="form-label text-muted label-step-4">Company Email Address (Optional)</label> 437 <input type="email" class="form-control mb-3" id="businessEmail" name="store[email]"> 438 </div> 439 </div> 440 <!-- <div class="row text-left product-sell"> 441 <label for="productsSold" class="form-label text-muted label-step-4">What products do you sell on Haroct? <span class="text-danger">*</span></label> 442 <a href="" class="btn border w-100 product-name"> 443 <div class="col text-left"> 444 <span class="product-name-label">Select</span> 445 <i class="ms-1 fa fa-angle-down text-right" aria-hidden="true"></i> 446 </div> 447 </a> 448 </div> --> 449 </div> 450 </div> 451 452 <div class="tab-pane fade" id="personal"> 453 <form> 454 <div class="row"> 455 <div class="col-md-6 text-left"> 456 <label for="personalName" class="form-label text-muted label-step-4">Name as per NRIC/Passport <span class="text-danger">*</span></label> 457 <input type="text" class="form-control mb-3 required" id="personalName" name="name"> 458 459 <label for="personalEmail" class="form-label text-muted label-step-4">Email Address (Optional)</label> 460 <input type="email" class="form-control mb-3" id="personalEmail" name="email"> 461 462 <div class="row mb-3"> 463 <div class="col"> 464 <label for="personalAddress" class="form-label text-muted label-step-4">Home Address (Optional)</label> 465 <div class="address-container border rounded p-2 d-flex justify-content-between align-items-center"> 466 <div class="text-haroct me-3"> 467 <i class="fa fa-map-marker" aria-hidden="true"></i> 468 </div> 469 <span id="personalAddressDisplay" class="text-muted">No address added yet</span> 470 <button type="button" class="btn btn-sm btn-outline-secondary" data-bs-toggle="modal" data-bs-target="#addAddressModal"> 471 Add </button> 472 </div> 473 <input type="hidden" id="personalAddress" name="personalAddress"> 474 </div> 475 </div> 476 </div> 477 <div class="col-md-6 text-left"> 478 <label for="personalIC" class="form-label text-muted label-step-4">IC/MyKad <span class="text-danger">*</span></label> 479 <input type="text" class="form-control mb-3 required" id="personalIC" name="idNo" required> 480 481 <label for="uploadIC" class="form-label text-muted label-step-4">Upload IC/Passport Copy (Optional)</label> 482 <div class="input-group form-control" onclick="document.getElementById('uploadIC').click()"> 483 <input type="file" class="form-control mb-3 visually-hidden" id="uploadIC" name="idDoc"> 484 <span class="cursor-pointer text-muted" id="fileNameDisplay"></span> 485 <span class="upload-icon-btn"> 486 <img src="/themes/store/default/assets/images/upload.png" alt="Upload Icon" class="icon-sq-20"> 487 </span> 488 </div> 489 </div> 490 </div> 491 </form> 492 </div> 493 494 <div class="form-check mt-3 text-left"> 495 <input class="form-check-input" type="checkbox" id="agreement" name="agreement" value="1" required> 496 <label class="form-check-label label-step-4" for="agreement"> 497 I have accepted the <a href="#" target="_blank">Agri Input Agreement</a> and <a href="https://haroct.com.my/privacy-policy" target="_blank">Privacy Policy</a>. 498 </label> 499 </div> 500 501 <div class="form-check mt-2 text-left"> 502 <input class="form-check-input" type="checkbox" id="declaration" name="declaration" value="1" required> 503 <label class="form-check-label label-step-4" for="declaration"> 504 I hereby declare that the information and documents provided are true, accurate, and belong to me. I understand that Haroct does not verify the authenticity of the uploaded documents, and I am fully responsible for any claims or liabilities arising from this declaration. 505 </label> 506 </div> 507 508 <div class="text-center"> 509 <button type="submit" class="btn btn-haroct mt-4 w-50">Sign Up Now</button> 510 </div> 511 </div> 512 </div> 513 </div> 514 </div> 515 </div> 516 </div> 517 </div> 518 </div> 519 </form> 520 521 <div class="modal fade" id="productNameModal" tabindex="-1" aria-labelledby="productNameModalLabel" aria-hidden="true"> 522 <div class="modal-dialog modal-dialog-centered"> 523 <div class="modal-content"> 524 <div class="modal-header border-bottom"> 525 <h5 class="modal-title">Select Product Name</h5> 526 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 527 </div> 528 <div class="modal-body"> 529 <div class="search-container mb-3"> 530 <input type="text" class="form-control search-input" placeholder="Search product name"> 531 </div> 532 <div class="product-list mb-3 scrollable-list"> 533 <div class="product-list-container"> 534 </div> 535 </div> 536 <a href="" class="btn btn-haroct rounded-3 w-100 confirm-product-name"> Confirm</a> 537 </div> 538 </div> 539 </div> 540 </div> 541 542 <div class="modal fade" id="addCompanyAddressModal" tabindex="-1" aria-labelledby="addCompanyAddressModalLabel" aria-hidden="true"> 543 <div class="modal-dialog modal-lg modal-dialog-centered"> 544 <div class="modal-content"> 545 <div class="d-flex justify-content-between align-items-center p-4"> 546 <h5 class="fw-bold">Add Company Address</h5> 547 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 548 </div> 549 <div class="p-4"> 550 <form id="companyAddressForm"> 551 <div class="row"> 552 <div class="col"> 553 <div class="mb-3"> 554 <label for="address1" class="form-label text-muted label-step-4">Address Line 1</label> 555 <input type="text" class="form-control" id="companyAddress1" name="store[address1]"> 556 </div> 557 <div class="mb-3"> 558 <label for="companyAddress2" class="form-label text-muted label-step-4">Address Line 2</label> 559 <input type="text" class="form-control" id="companyAddress2" name="store[address2]"> 560 </div> 561 <div class="mb-3"> 562 <label for="state" class="form-label text-muted label-step-4">State</label> 563 <select class="form-select" id="stateSelectBusiness" name="store[state]" required> 564 <option value="" selected>Select State</option> 565 </select> 566 </div> 567 <div class="mb-3"> 568 <label for="city" class="form-label text-muted label-step-4">City</label> 569 <select class="form-select" name="store[city]" id="citySelectBusiness" required> 570 <option value="" selected>Select City</option> 571 </select> 572 </div> 573 <div class="mb-3"> 574 <label for="postcode" class="form-label text-muted label-step-4">Postcode</label> 575 <input type="text" class="form-control" id="postcode" name="store[postcode]"> 576 </div> 577 <input type="text" class="form-control" id="accessToken" name="accessToken" value="" hidden> 578 <input type="text" class="form-control" id="type" name="type" value="store" hidden> 579 </div> 580 <div class="col"> 581 <div class="mb-3"> 582 <label class="form-label text-muted label-step-4">Map</label>
583 <p class="map-instruction text-muted label-step-4">Move the pin on the map for accurate location</p> 584 <div class="map-container-company"></div> 585 </div> 586 <div> 587 <input type="text" class="form-control" id="latCompany" name="lat" hidden> 588 <input type="text" class="form-control" id="lngCompany" name="lng" hidden> 589 </div> 590 </div> 591 </div> 592 <div class="d-flex justify-content-center align-items-center mt-4"> 593 <button type="button" class="btn bg-white border w-25 me-3" data-bs-dismiss="modal" aria-label="Close">Cancel</button> 594 <button type="submit" class="btn btn-haroct text-haroct w-25">Save</button> 595 </div> 596 </form> 597 </div> 598 </div> 599 </div> 600 </div> 601 602 <div class="modal fade" id="addAddressModal" tabindex="-1" aria-labelledby="addAddressModalLabel" aria-hidden="true"> 603 <div class="modal-dialog modal-lg modal-dialog-centered"> 604 <div class="modal-content"> 605 <div class="d-flex justify-content-between align-items-center p-4"> 606 <h5 class="fw-bold">Add Home Address</h5> 607 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 608 </div> 609 <div class="p-4"> 610 <form id="addressForm"> 611 <div class="row"> 612 <div class="col"> 613 <div class="mb-3"> 614 <label for="addressName" class="form-label text-muted label-step-4">Name</label> 615 <input type="text" class="form-control" id="nameAddress" name="delivery[name]"> 616 </div> 617 <div class="mb-3"> 618 <label for="personalContact" class="form-label text-muted label-step-4">Contact Number </label> 619 <div class="phone-input-container"> 620 <input type="tel" id="contact" name="delivery[phone]" class="form-control required" placeholder=" "> 621 <input type="hidden" name="delivery[countryCode]" value=""> 622 </div> 623 </div> 624 <div class="mb-3"> 625 <label for="address1" class="form-label text-muted label-step-4">Address Line 1</label> 626 <input type="text" class="form-control" id="address1" name="delivery[address]"> 627 </div> 628 <div class="mb-3"> 629 <label for="Address2" class="form-label text-muted label-step-4">Address Line 2</label> 630 <input type="text" class="form-control" id="address2" name="delivery[addressLine2]]"> 631 </div> 632 <div class="mb-3"> 633 <label for="state" class="form-label text-muted label-step-4">State</label> 634 <select class="form-select" id="stateSelectPersonal" name="delivery[locationState]" required> 635 <option value="" selected>Select State</option> 636 </select> 637 </div> 638 <div class="mb-3"> 639 <label for="city" class="form-label text-muted label-step-4">City</label> 640 <select class="form-select" name="delivery[locationCity]" id="citySelectPersonal" required> 641 <option value="" selected>Select City</option> 642 </select> 643 </div> 644 <div class="mb-3"> 645 <label for="postcode" class="form-label text-muted label-step-4">Postcode</label> 646 <input type="text" class="form-control" id="personalPostcode" name="delivery[postcode]"> 647 </div> 648 <input type="text" class="form-control" id="accessToken" name="accessToken" value="" hidden> 649 <input type="text" class="form-control" id="type" name="type" value="store" hidden> 650 </div> 651 <div class="col"> 652 <div class="mb-3"> 653 <label class="form-label text-muted label-step-4">Map</label>
654 <p class="map-instruction text-muted label-step-4">Move the pin on the map for accurate location</p> 655 <div class="map-container-personal"></div> 656 </div> 657 <div> 658 <input type="text" class="form-control" id="lat" name="lat" hidden> 659 <input type="text" class="form-control" id="lng" name="lng" hidden> 660 </div> 661 </div> 662 </div> 663 <div class="d-flex justify-content-center align-items-center mt-4"> 664 <button type="button" class="btn bg-white border w-25 me-3" data-bs-dismiss="modal" aria-label="Close">Cancel</button> 665 <button type="submit" class="btn btn-haroct text-haroct w-25">Save</button> 666 </div> 667 </form> 668 </div> 669 </div> 670 </div> 671 </div> 672 </section> 673 </aside> 674 </div> 675 </div> 676 </div> 677 678<div class="modal fade" id="otpModal" tabindex="-1" aria-labelledby="otpModalLabel" aria-hidden="true" data-bs-backdrop="static"> 679 <div class="modal-dialog modal-md modal-dialog-centered"> 680 <div class="modal-content rounded-4"> 681 <div class="modal-header border-bottom-0"> 682 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 683 </div> 684 <div class="modal-body text-center"> 685 <div class="text-center mb-3"><img src="/themes/store/default/assets/images/2fa.png"></div> 686 <div class="fs-5 mb-2">Verify Your Phone</div> 687 <p class="text-muted small mb-4">Code has been sent to <span class="fw-bold text-haroct phone-number"></span></p> 688 <div class="otp-inputs mb-4 d-flex justify-content-center gap-2"> 689 <input type="text" maxlength="1" class="otp-pin" /> 690 <input type="text" maxlength="1" class="otp-pin" /> 691 <input type="text" maxlength="1" class="otp-pin" /> 692 <input type="text" maxlength="1" class="otp-pin" /> 693 <input type="text" maxlength="1" class="otp-pin" /> 694 <input type="text" maxlength="1" class="otp-pin" /> 695 </div> 696 <div class="row mb-3 otp-countdown d-none"> 697 <div class="col text-center"> 698 <small class="text-warning">Resend OTP in <span>5:00</span></small> 699 </div> 700 </div> 701 <button type="button" class="btn btn-sm btn-link text-decoration-none d-none btn-resend-otp">Resend OTP</button> 702 </div> 703 <div class="modal-footer justify-content-center border-top-0"> 704 <button type="button" class="btn btn-haroct btn-verify-otp w-100" disabled>Verify & Continue</button> 705 </div> 706 </div> 707 </div> 708</div> 709 710<div class="modal fade rounded-5" id="referralModal" tabindex="-1" aria-labelledby="referralModalLabel" aria-hidden="true"> 711 <div class="modal-dialog modal-sm modal-dialog-centered modal-dialog-scrollable"> 712 <div class="modal-content rounded-4"> 713 <div class="modal-body"> 714 <form id="referralForm"> 715 <div class="mb-3 link-haroct text-center"> 716 <label for="referral-code" class="col-form-label"> 717 <i class="fa fa-gift fs-1 mt-4 mb-2"></i> 718 <h6>Enter Referral code<br>to claim your benefits</h6> 719 </label> 720 <input type="text" class="form-control" id="referral-code" placeholder="Referral code"> 721 </div> 722 </form> 723 </div> 724 <div class="modal-footer"> 725 <button type="button" class="btn btn-sm btn-secondary" data-bs-dismiss="modal">Close</button> 726 <button type="button" class="btn btn-sm btn-haroct btn-submit-referral">Submit</button> 727 </div> 728 </div> 729 </div> 730</div> 731 732<div class="modal fade rounded-5" id="emailModal" tabindex="-1" aria-labelledby="emailModalLabel" aria-hidden="true"> 733 <div class="modal-dialog modal-md">
734 <div class="modal-content rounded-4"> 735 <div class="d-flex justify-content-end align-items-center p-2"> 736 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 737 </div> 738 <div class="modal-body text-center"> 739 <div class="mb-3 link-haroct"> 740 <img src="/themes/store/default/assets/images/icon-coin-signup.svg"> 741 <h4 class="fw-bold">Secure Your Account & Earn 50 Coins!</h4> 742 </div> 743 <div> 744 <p class="text-muted">Add your email to stay updated on your orders,<br>get special deals, and keep your account secure.</p> 745 </div> 746 </div> 747 <div class="modal-footer"> 748 <div class="row w-100 mb-2"> 749 <div class="col-12"> 750 <button type="button" class="btn btn-haroct w-100 py-2">Add Email Now</button> 751 </div> 752 </div> 753 <div class="row w-100"> 754 <div class="col-12 text-center"> 755 <a href="#" class="link-haroct" data-bs-dismiss="modal"><u>It's okay, I will do it later</u></a> 756 </div> 757 </div> 758 </div> 759 </div> 760 </div> 761</div> 762 763<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/build/css/intlTelInput.css">
vendor: 70 bytes, lines 763-766
763 764 765 766<script src="https://www.google.com/recaptcha/enterprise.js?render=
7666Lcim1osAAAAANvZ5krlNRs6QP11OT71fvoj2ous
vendor: 12 bytes, line 766
766"></script>
767<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyAvvOIrBV014ZdBqnUKpjT82Ae46A90bmA&libraries=places&callback=initAutocomplete" async defer></script>
vendor: 1 bytes, line 767
767
768<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/js/intlTelInput.min.js"></script>
vendor: 1 bytes, line 768
768
769<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/js/utils.js"></script>
vendor: 1 bytes, line 769
769
770<script> 771var avatar = ''; 772 773$(document).ready(function() { 774 function getURLParameter(name) { 775 return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search) || [null, ''])[1].replace(/\+/g, '%20')) || null; 776 } 777 778 // Validate parentId invitation on page load 779 var parentIdParam = null; 780 if (parentIdParam) { 781 ajax_form({ parentId: parentIdParam }, 'https://haroct.com.my/api/v1/u/get-user-info').done(function(response) { 782 if (response.code == 200 && response.item) { 783 $('#inviter-name').text(response.item.name || ''); 784 $('#invitation-banner').removeClass('d-none'); 785 } else { 786 t('e', 'This invitation link is invalid or has expired.'); 787 $('.btn-request-otp, .next-btn').prop('disabled', true); 788 } 789 }).fail(function() { 790 t('e', 'Unable to validate invitation. Please try again.'); 791 }); 792 } 793 794 $('#seller-fields').addClass('d-none'); 795 $('#buyer-fields').addClass('d-none'); 796 797 $('[name="accountType"]').change(function() { 798 const selectedRole = $('[name="accountType"]:checked').val(); 799 800 if (selectedRole == 1) { 801 // Show seller fields and hide buyer fields 802 $('#seller-fields').removeClass('d-none'); 803 $('#buyer-fields').addClass('d-none'); 804 } else if (selectedRole == 0) { 805 // Show buyer fields and hide seller fields 806 $('#buyer-fields').removeClass('d-none'); 807 $('#seller-fields').addClass('d-none'); 808 } else { 809 // Hide both fields if no role is selected 810 $('#seller-fields').addClass('d-none'); 811 $('#buyer-fields').addClass('d-none'); 812 } 813 }); 814 815 $('.role-box').hover(function() { 816 const role = $(this).data('role'); 817 let hoverText = ''; 818 if (role === 'buyer') { 819 hoverText = 'As a buyer, you can use Haroct to purchase products in bulk with good price'; 820 } else if (role === 'seller') { 821 hoverText = 'As a seller, you can use Haroct to sell products with premium prices'; 822 } 823 $(this).attr('data-hover-text', hoverText); 824 }); 825 826 let currentStep = parseInt(getURLParameter('step')) || 1; 827 const totalSteps = 5; 828 const stepWidth = 100 / totalSteps; 829 830 $('.form-step').hide(); 831 $('#step' + currentStep).show(); 832 $('#back-btn').hide(); 833 updateProgress(); 834 835 window.proceedToStep2 = function() { 836 $('#step1').hide(); 837 $('#step2').show(); 838 currentStep++; 839 updateProgress(); 840 }; 841 842 function checkFields(step) { 843 let isValid = true; 844 let failingFields = []; 845 846 const activeForm = step.find('.normal-form:visible, .agri-input-form:visible'); 847 const activeTab = step.find('.tab-pane.active'); 848 849 activeTab.find('.required').each(function() { 850 const $field = $(this); 851 const fieldName = $field.attr('name') || $field.attr('id'); 852 853 if ($field.is(':radio')) { 854 if ($('input[name="' + $field.attr('name') + '"]:checked').length === 0) { 855 isValid = false; 856 failingFields.push(`Radio: ${fieldName} - not checked`); 857 } 858 } else if ($field.is(':checkbox')) { 859 if (!$field.is(':checked')) { 860 isValid = false; 861 failingFields.push(`Checkbox: ${fieldName} - not checked`); 862 } 863 } else { 864 const value = $field.val(); 865 if (!value || value.trim() === '') { 866 isValid = false; 867 failingFields.push(`Input: ${fieldName} - empty value`); 868 } 869 } 870 }); 871 872 // Additional validation for step 1 873 if (step.attr('id') === 'step1') { 874 const phone = $('[name="phone"]').val(); 875 const isPhoneValid = phone && phone.trim() !== ''; 876 const tncChecked = $('#tnc').is(':checked'); 877 878 // For Thailand, "Request OTP" acts as a continue button, so change its text 879 const countryCode = $('[name="countryCode"]').val(); 880 if (countryCode === '+66') { 881 $('.btn-request-otp').text('Continue'); 882 } else { 883 $('.btn-request-otp').text('Request OTP'); 884 } 885 886 if (!isPhoneValid || !tncChecked) { 887 isValid = false;
888 } 889 } 890 891 // console.log('Validation Results:', { 892 // stepNumber: step.attr('id'), 893 // isValid: isValid, 894 // failingFields: failingFields 895 // }); 896 897 step.find('.next-btn, .btn-request-otp').prop('disabled', !isValid); 898 899 if (step.attr('id') === 'step4') { 900 const isAgriInput = $('.agri-input-product').is(':visible'); 901 902 if (isAgriInput) { 903 // Validate at least one product is selected 904 if ($('.agri-product-checkbox:checked').length === 0) { 905 isValid = false; 906 t('e', 'Please select at least one product'); 907 } 908 } else { 909 // Normal product selection validation 910 if (!$('[name="verticalType"]:checked').length) { 911 isValid = false; 912 } 913 } 914 } 915 916 // Check if we're in step 4 and which form is active 917 if (step.attr('id') === 'step5') { 918 const isAgriInputForm = $('#step5 .agri-input-form').is(':visible'); 919 920 if (isAgriInputForm) { 921 // Agri Input form validation 922 const isBusinessTab = $('#business').hasClass('active'); 923 const activeContainer = isBusinessTab ? $('#business') : $('#personal'); 924 925 activeContainer.find('.required').each(function() { 926 if (!validateField($(this))) { 927 isValid = false; 928 failingFields.push($(this).attr('name') || $(this).attr('id')); 929 } 930 }); 931 } else { 932 // Normal form validation (only state and city) 933 $('#stateSelect, #citySelect').each(function() { 934 if (!validateField($(this))) { 935 isValid = false; 936 failingFields.push($(this).attr('name')); 937 } 938 }); 939 } 940 } else { 941 // Validation for other steps 942 step.find('.required').each(function() { 943 if (!validateField($(this))) { 944 isValid = false; 945 failingFields.push($(this).attr('name') || $(this).attr('id')); 946 } 947 }); 948 } 949 950 // console.log('Validation Results:', { 951 // stepNumber: step.attr('id'), 952 // isValid: isValid, 953 // failingFields: failingFields 954 // }); 955 956 return isValid; 957 } 958 959 function validateField(field) { 960 if (field.is(':radio')) { 961 return field.is(':checked') || $('input[name="' + field.attr('name') + '"]:checked').length > 0; 962 } else if (field.is(':checkbox')) { 963 return field.is(':checked'); 964 } else { 965 const value = field.val(); 966 return value && value.trim() !== ''; 967 } 968 } 969 970 // Check fields on load in case of any pre-filled values 971 $('.form-step').each(function() { 972 checkFields($(this)); 973 }); 974 975 $('.required').on('change keyup', function() { 976 let step = $(this).closest('.form-step'); 977 checkFields(step); 978 }); 979 980 $('.next-btn').click(function() { 981 let current = $(this).closest('.form-step'); 982 let next = current.next('.form-step'); 983 984 if (!checkFields(current)) { 985 //return; 986 } 987 988 if (current.attr('id') === 'step2') { 989 if( $('.pin').val() === "" ){ 990 t('e', 'Please set your passcode'); 991 return false; 992 } 993 } 994 995 // For other steps, proceed normally 996 if (next.length) { 997 current.hide(); 998 next.show(); 999 currentStep++; 1000 updateProgress(); 1001 } 1002 }); 1003 1004 $('#back-btn').click(function() { 1005 let current = $('.form-step:visible'); 1006 let prev = current.prev('.form-step'); 1007 if (prev.length) { 1008 current.hide(); 1009 prev.show(); 1010 currentStep--; 1011 updateProgress(); 1012 } 1013 }); 1014 1015 // Function to handle accountSegment change 1016 function handleAccountSegmentChange() { 1017 const selectedSegment = $('[name="accountSegment"]:checked').val(); 1018 // console.log('Selected Segment:', selectedSegment); // Debugging: Log the selected segment 1019 1020 if (selectedSegment == 106 || selectedSegment == 205) { 1021 // console.log('Agri Input Segment Selected'); 1022 // Show Agri Input Product options and hide normal options 1023 $('.agri-input-product').removeClass('d-none'); 1024 $('.non-agri-product').addClass('d-none'); 1025 1026 // Show Agri Input Form and hide Normal Form 1027 $('#step4 .agri-input-form').removeClass('d-none'); 1028 $('#step4 .normal-form').addClass('d-none'); 1029 1030 // Disable validation for normal form fields 1031 $('.normal-form .required').prop('disabled', true); 1032 // Enable validation for Agri input form fields 1033 $('.agri-input-form .required').prop('disabled', false); 1034 1035 $('#stateSelect, #citySelect').prop('disabled', true); 1036 $('#agreement, #declaration').prop('required', true); 1037 1038 fetchAgriInputProducts(); 1039 1040 } else { 1041 // console.log('Normal Segment Selected'); 1042 // Show normal options and hide Agri Input Product options 1043 $('.agri-input-product').addClass('d-none'); 1044 $('.non-agri-product').removeClass('d-none'); 1045 1046 // Show Normal Form and hide Agri Input Form 1047 $('#step4 .normal-form').removeClass('d-none'); 1048 $('#step4 .agri-input-form').addClass('d-none'); 1049 1050 // Disable validation for Agri input form fields 1051 $('.agri-input-form .required').prop('disabled', true); 1052 // Enable validation for normal form fields 1053 $('.normal-form .required').prop('disabled', false); 1054 1055 $('#stateSelect, #citySelect').prop('disabled', false); 1056 $('#agreement, #declaration').prop('required', false); 1057 } 1058 } 1059 1060 // Trigger the change event initially to set the correct visibility 1061 $('[name="accountSegment"]').trigger('change'); 1062 1063 $('a[data-bs-toggle="tab"]').on('shown.bs.tab', function (e) { 1064 const isBusinessForm = $(e.target).data('is-business'); 1065 1066 $('.tab-pane').removeClass('show active'); 1067 1068 if (isBusinessForm) { 1069 $('#business').addClass('show active'); 1070 $('#personal').removeClass('show active'); 1071 } else { 1072 $('#personal').addClass('show active'); 1073 $('#business').removeClass('show active'); 1074 } 1075 }); 1076 1077 // Trigger the tab change event initially to set the correct form visibility 1078 $('a[data-bs-toggle="tab"].active').trigger('shown.bs.tab'); 1079 1080 function handleProductSellVisibility() { 1081 const selectedSegment = $('[name="accountSegment"]:checked').val(); 1082 1083 if (selectedSegment == 106) { 1084 // Hide the "What products do you sell on Haroct?" section 1085 $('.product-sell').hide(); 1086 } else { 1087 // Show the "What products do you sell on Haroct?" section 1088 $('.product-sell').show(); 1089 } 1090 } 1091 1092 // Function to update Step 3 heading based on accountType (buyer/seller) 1093 function updateStep4Heading() { 1094 const selectedRole = $('[name="accountType"]:checked').val(); 1095 const step4Heading = $('#step4 h4'); 1096 1097 if (selectedRole == 0) { 1098 // Buyer role 1099 step4Heading.text('What do you want to buy?'); 1100 } else if (selectedRole == 1) { 1101 // Seller role 1102 step4Heading.text('What do you sell?'); 1103 } 1104 } 1105 1106 // Listen for changes in accountType (buyer/seller) 1107 $('[name="accountType"]').change(function() { 1108 updateStep4Heading(); 1109 }); 1110 1111 // Trigger the change event initially to set the correct heading 1112 $('[name="accountType"]').trigger('change'); 1113 1114 // Trigger the change event initially to set the correct visibility 1115 $('[name="accountSegment"]').trigger('change'); 1116 1117 $(document).on('change', '#uploadIC', function() { 1118 const file = this.files[0]; 1119 if (!file) return; 1120 1121 $('#fileNameDisplay').text(file.name); 1122 1123 const reader = new FileReader(); 1124 reader.onload = function(e) { 1125 const base64Data = e.target.result.split(',')[1]; 1126 const mimeType = file.type; 1127 const formattedData = `data:${mimeType};base64,${base64Data}`; 1128 1129 $('[name="idDoc[data]"]').remove(); 1130 $('<input>').attr({ 1131 type: 'hidden', 1132 name: 'idDoc[data]', 1133 value: formattedData 1134 }).appendTo('#signupForm'); 1135 };
1136 reader.readAsDataURL(file); 1137 }); 1138 1139 function updateProgress() { 1140 // Update step info text 1141 $('#step-info').text(`${currentStep} of ${totalSteps}`); 1142 1143 // Show or hide the back button based on the current step 1144 $('#back-btn').toggle(currentStep !== 1); 1145 1146 // Update the progress bar width 1147 $('.progress-bar') 1148 .css('width', `${stepWidth * currentStep}%`) 1149 .attr('aria-valuenow', stepWidth * currentStep); 1150 1151 // Remove active state from all steps and connectors 1152 $('.step, .connector').removeClass('active'); 1153 1154 // Add active state to the current step and all previous steps 1155 $('.step').each(function (index) { 1156 if (index + 1 <= currentStep) { 1157 $(this).addClass('active'); 1158 } 1159 }); 1160 1161 // Add active state to the connectors for all completed steps 1162 $('.connector').each(function (index) { 1163 if (index + 1 < currentStep) { 1164 $(this).addClass('active'); 1165 } 1166 }); 1167 1168 $('.pin').each(function (index, element) { 1169 $(element).on('input', function () { 1170 const value = this.value; 1171 1172 // Move forward when filled 1173 if (value.length === 1 && index < $('.pin').length - 1) { 1174 $('.pin').eq(index + 1).focus(); 1175 } 1176 1177 // Move backward when cleared 1178 if (value.length === 0 && index > 0) { 1179 $('.pin').eq(index - 1).focus(); 1180 } 1181 1182 checkPinComplete(); 1183 }); 1184 1185 $(element).on('keydown', function (e) { 1186 // Safety fallback for Backspace 1187 if (e.key === 'Backspace' && !this.value && index > 0) { 1188 $('.pin').eq(index - 1).focus(); 1189 } 1190 }); 1191 }); 1192} 1193 1194 $(document).on('change', '[name="lang"]', function(e) { 1195 var val = $(this).val(); 1196 if (val) { 1197 var searchParams = new URLSearchParams(window.location.search); 1198 searchParams.set('lang', val); 1199 var newUrl = window.location.protocol + '//' + window.location.host + window.location.pathname + '?' + searchParams.toString(); 1200 window.location.href = newUrl; 1201 } 1202 }); 1203 1204 1205 $(document).on('change', '[name="accountType"]', function(e){ 1206 val = $('[name="accountType"]:checked').data('info'); 1207 if(val){ 1208 $('.block-role-info span').text(val).parent().removeClass('d-none').show(); 1209 }else{ 1210 $('.block-role-info span').text('').parent().addClass('d-none'); 1211 } 1212 }); 1213 1214 getProductName(); 1215 1216 function fetchAccountSegments() { 1217 const requestData = { 1218 'types[]': 'customer-account-segment', // Adding the required parameter 1219 lang: '' 1220 }; 1221 1222 ajax_form(requestData, 'https://haroct.com.my/api/v1/g/get-settings').done(function(response) { 1223 if (response.code == 200) { 1224 // console.log('segment', response); 1225 renderAccountSegments(response.settings['customer-account-segment']); // Ensure correct reference 1226 } else { 1227 t('e', response.msg); 1228 } 1229 }).fail(function(jqXHR, textStatus, errorThrown) { 1230 ajax_error(jqXHR); 1231 }); 1232} 1233 1234 1235 // Render account segments based on the fetched data 1236 function renderAccountSegments(segments) { 1237 const buyerSegments = segments.buyer; 1238 const sellerSegments = segments.seller; 1239 1240 // Render buyer segments 1241 renderSegments(buyerSegments, 'buyer-fields'); 1242 1243 // Render seller segments 1244 renderSegments(sellerSegments, 'seller-fields'); 1245 } 1246
1247 function renderSegments(segments, containerId) { 1248 const container = $(`#${containerId} .row`); 1249 container.empty(); // Clear existing content 1250 1251 // Convert object to an array and split into chunks of 3 1252 const segmentChunks = chunkArray(Object.entries(segments), 3); 1253 1254 segmentChunks.forEach(rowSegments => { 1255 const rowHtml = $('<div class="row mb-3"></div>'); 1256 1257 rowSegments.forEach(([key, value]) => { 1258 const segmentHtml = ` 1259 <div class="col-md-4"> 1260 <div class="radio-box text-center"> 1261 <input type="radio" class="required" id="${containerId}-${key}" name="accountSegment" value="${key}"> 1262 <label for="${containerId}-${key}" class="p-1 p-md-3 w-100"> 1263 <div class="mt-2 label-field">${value}</div> 1264 </label> 1265 </div> 1266 </div> 1267 `; 1268 rowHtml.append(segmentHtml); 1269 }); 1270 1271 container.append(rowHtml); 1272 }); 1273 1274 // Bind the change event to accountSegment radio buttons after rendering 1275 $('[name="accountSegment"]').change(function() { 1276 // console.log('Account Segment Changed:', $(this).val()); // Debugging: Log the selected segment 1277 handleAccountSegmentChange(); 1278 updateStep4Heading(); 1279 handleProductSellVisibility(); 1280 }); 1281} 1282 1283// Helper function to split array into chunks 1284function chunkArray(arr, chunkSize) { 1285 return arr.reduce((acc, _, i) => 1286 (i % chunkSize ? acc[acc.length - 1].push(arr[i]) : acc.push([arr[i]]), acc), 1287 []); 1288} 1289 1290 1291 // Fetch account segments on page load 1292 fetchAccountSegments(); 1293 1294 setTimeout(function() { 1295 if ($('[name="accountSegment"]').length > 0) { 1296 // console.log('Account Segment radio buttons exist in the DOM.'); 1297 } else { 1298 console.log('Account Segment radio buttons do NOT exist in the DOM.'); 1299 } 1300 }, 1000); // Delay to allow dynamic rendering 1301 1302 setTimeout(function() { 1303 $('[name="accountSegment"]').first().trigger('click'); 1304 }, 1000); 1305 1306 // Handle role change (buyer/seller) 1307 $('[name="accountType"]').change(function() { 1308 const selectedRole = $('[name="accountType"]:checked').val(); 1309 if (selectedRole == 1) { 1310 $('#seller-fields').removeClass('d-none'); 1311 $('#buyer-fields').addClass('d-none'); 1312 } else if (selectedRole == 0) { 1313 $('#buyer-fields').removeClass('d-none'); 1314 $('#seller-fields').addClass('d-none'); 1315 } 1316 }); 1317 1318 function toggleRequiredFields() { 1319 const activeTab = $('.tab-pane.active'); // Get the active tab 1320 const inactiveTabs = $('.tab-pane').not('.active'); // Get inactive tabs 1321 1322 // Enable required attributes for fields in the active tab 1323 activeTab.find('.required').prop('required', true); 1324 1325 // Disable required attributes for fields in inactive tabs 1326 inactiveTabs.find('.required').prop('required', false); 1327 } 1328 1329 // Trigger the toggle function when switching tabs 1330 $('a[data-bs-toggle="tab"]').on('shown.bs.tab', function () { 1331 toggleRequiredFields(); 1332 }); 1333 1334 // Call the function on page load to set the correct state 1335 toggleRequiredFields(); 1336 1337 1338 // Function to fetch product names using ajax_form 1339 function getProductName(page = 1, keyword = '') { 1340 const requestData = { 1341 page: page, 1342 search: keyword, 1343 parent: 820 1344 }; 1345 1346 // Use ajax_form to se\nd the request 1347 ajax_form(requestData, 'https://haroct.com.my/api/v1/products/get-categories').done(function(response) { 1348 if (response.code == 200 && response.items) { 1349 // Clear existing options except the first one 1350 $('#productsSold, #productsSoldPersonal').find('option').not(':first').remove(); 1351 1352 // Append new options 1353 response.items.forEach(item => { 1354 $('#productsSold, #productsSoldPersonal').append(new Option(item.name, item.id)); 1355 }); 1356 } else { 1357 t('e', response.msg); 1358 } 1359 }).fail(function(jqXHR, textStatus, errorThrown) { 1360 ajax_error(jqXHR); 1361 }); 1362 } 1363 1364 $('#productsSold').on('change', function() { 1365 const selectedProductId = $(this).val(); 1366 const selectedProductName = $(this).find('option:selected').text(); 1367 1368 // You can now use selectedProductId and selectedProductName for further processing 1369 // console.log('Selected Product ID:', selectedProductId); 1370 // console.log('Selected Product Name:', selectedProductName); 1371 }); 1372 1373 // Handle product selection 1374 $('#productsSoldPersonal').on('change', function() { 1375 const selectedProductId = $(this).val(); 1376 const selectedProductName = $(this).find('option:selected').text(); 1377 1378 // You can now use selectedProductId and selectedProductName for further processing 1379 // console.log('Selected Product ID:', selectedProductId); 1380 // console.log('Selected Product Name:', selectedProductName); 1381 }); 1382 1383 function fetchAgriInputProducts() { 1384 const requestData = { 1385 parent: 820 // Using the specified verticalId 1386 }; 1387 1388 ajax_form(requestData, 'https://haroct.com.my/api/v1/products/get-categories').done(function(response) { 1389 if (response.code == 200 && response.items) { 1390 renderAgriInputProducts(response.items); 1391 } else { 1392 t('e', response.msg || 'Failed to load agricultural input products'); 1393 } 1394 }).fail(function(jqXHR, textStatus, errorThrown) { 1395 ajax_error(jqXHR); 1396 }); 1397} 1398 1399// Function to render agri input products 1400function renderAgriInputProducts(products) { 1401 const container = $('#agriProductContainer'); 1402 container.empty(); // Clear existing content 1403
1404 products.forEach(product => { 1405 const productHtml = ` 1406 <div class="col-12 col-md-3 agri-product-item mb-3"> 1407 <div class="agri-product-box"> 1408 <input type="checkbox" class="agri-product-checkbox" 1409 id="agri-${product.id}" name="verticalType" 1410 value="${product.id}" data-product-name="${product.name}"> 1411 <label for="agri-${product.id}"> 1412 <img class="agri-image" src="${product.image || '/themes/store/default/assets/images/default-product.png'}"> 1413 <div class="mt-2">${product.name}</div> 1414 </label> 1415 </div> 1416 </div> 1417 `; 1418 container.append(productHtml); 1419 }); 1420} 1421 1422fetchAgriInputProducts(); 1423 1424$(document).on('change', '.agri-product-checkbox', function() { 1425 const $box = $(this).closest('.agri-product-box'); 1426 if ($(this).is(':checked')) { 1427 $box.addClass('selected'); 1428 } else { 1429 $box.removeClass('selected'); 1430 } 1431 1432 // Update the form validation 1433 checkFields($('#step4')); 1434}); 1435 1436// Cross-border interest step: "Where is your business based?" (single choice) + 1437// "Which market do you want to buy/sell from/to?" (multi-select, excludes the chosen home country). 1438var marketCountries = []; 1439 1440function fetchMarketCountries() { 1441 ajax_form({ limit: 100 }, 'https://haroct.com.my/api/v1/g/get-location-countries').done(function(response) { 1442 if (response.code == 200 && response.items) { 1443 marketCountries = response.items; 1444 renderBusinessCountryOptions(); 1445 } else { 1446 t('e', response.msg); 1447 } 1448 }).fail(function(jqXHR, textStatus, errorThrown) { 1449 ajax_error(jqXHR); 1450 }); 1451} 1452 1453function renderBusinessCountryOptions() { 1454 const container = $('#businessCountryOptions'); 1455 const previouslyChecked = $('[name="businessCountryId"]:checked').val(); 1456 container.empty(); 1457 1458 marketCountries.forEach(function(country) { 1459 const isChecked = previouslyChecked ? (previouslyChecked == country.id) : !!country.isDefault; 1460 const optionHtml = ` 1461 <div class="col-auto mb-2"> 1462 <div class="radio-box text-center px-3"> 1463 <input type="radio" class="required" id="business-country-${country.id}" name="businessCountryId" value="${country.id}" ${isChecked ? 'checked' : ''}> 1464 <label for="business-country-${country.id}" class="p-1 p-md-2 px-md-4 w-100"> 1465 <div class="fw-bold">${country.name}</div> 1466 </label> 1467 </div> 1468 </div> 1469 `; 1470 container.append(optionHtml); 1471 }); 1472 1473 renderInterestedMarketOptions(); 1474} 1475 1476function renderInterestedMarketOptions() { 1477 const container = $('#interestedMarketOptions'); 1478 const businessCountryId = $('[name="businessCountryId"]:checked').val(); 1479 const previouslyChecked = $('[name="interestedMarketIds[]"]:checked').map(function() { 1480 return $(this).val(); 1481 }).get(); 1482 container.empty(); 1483 1484 marketCountries.forEach(function(country) { 1485 if (businessCountryId && businessCountryId == country.id) { 1486 return; 1487 } 1488 1489 const isChecked = previouslyChecked.indexOf(String(country.id)) !== -1; 1490 const rowHtml = ` 1491 <div class="d-flex align-items-center justify-content-between border rounded-3 p-3 mb-2"> 1492 <label class="mb-0" for="market-${country.id}">${country.name}</label> 1493 <input type="checkbox" id="market-${country.id}" name="interestedMarketIds[]" value="${country.id}" style="width:18px;height:18px;flex:none;" ${isChecked ? 'checked' : ''}> 1494 </div> 1495 `; 1496 container.append(rowHtml); 1497 }); 1498 1499 updateMarketStepButton(); 1500} 1501 1502function updateMarketStepButton() { 1503 $('#stepMarket .next-btn').prop('disabled', !$('[name="businessCountryId"]:checked').length); 1504} 1505 1506function updateMarketQuestionHeading() { 1507 const selectedRole = $('[name="accountType"]:checked').val(); 1508 const heading = $('#marketQuestionHeading'); 1509 1510 if (selectedRole == 1) { 1511 heading.text('Which market do you want to sell to?'); 1512 } else { 1513 heading.text('Which market do you want to buy from?'); 1514 } 1515} 1516 1517$(document).on('change', '[name="businessCountryId"]', function() { 1518 renderInterestedMarketOptions(); 1519}); 1520 1521$(document).on('change', '[name="interestedMarketIds[]"]', function() { 1522 updateMarketStepButton(); 1523}); 1524 1525$('[name="accountType"]').on('change', updateMarketQuestionHeading); 1526updateMarketQuestionHeading(); 1527 1528fetchMarketCountries(); 1529 1530 //reside 1531 1532 ajax_form({country:1, limit: 100}, 'https://haroct.com.my/api/v1/g/get-location-states').done(function(response) { 1533 // console.log('state:' ,response); 1534 if (response.code == 200) {
1535 response.items.forEach(function(state) { 1536 $('#stateSelect').append(`<option value="${state.id}">${state.name}</option>`); 1537 }); 1538 } else { 1539 t('e', response.msg); 1540 } 1541 }).fail(function(jqXHR, textStatus, errorThrown) { 1542 ajax_error(jqXHR); 1543 }); 1544 1545 $('#stateSelect').change(function() { 1546 var stateId = $(this).val(); 1547 $('#citySelect').empty().append('<option value="" selected>Select City</option>'); 1548 1549 ajax_form({state:stateId, limit: 100}, 'https://haroct.com.my/api/v1/g/get-location-cities').done(function(response) { 1550 // console.log('city:' ,response); 1551 if (response.code == 200) { 1552 response.items.forEach(function(city) { 1553 $('#citySelect').append(`<option value="${city.id}">${city.name}</option>`); 1554 }); 1555 } else { 1556 t('e', response.msg); 1557 } 1558 }).fail(function(jqXHR, textStatus, errorThrown) { 1559 ajax_error(jqXHR); 1560 }); 1561 }); 1562 1563 // Fetch states for both business and personal forms 1564ajax_form({country:1}, 'https://haroct.com.my/api/v1/g/get-location-states').done(function(response) { 1565 if (response.code == 200) { 1566 response.items.forEach(function(state) { 1567 $('#stateSelectBusiness, #stateSelectPersonal').append(`<option value="${state.id}">${state.name}</option>`); 1568 }); 1569 } else { 1570 t('e', response.msg); 1571 } 1572}).fail(function(jqXHR, textStatus, errorThrown) { 1573 ajax_error(jqXHR); 1574}); 1575 1576// Handle state change for business address 1577$('#stateSelectBusiness').change(function() { 1578 var stateId = $(this).val(); 1579 $('#citySelectBusiness').empty().append('<option value="" selected>Select City</option>'); 1580 1581 ajax_form({state:stateId}, 'https://haroct.com.my/api/v1/g/get-location-cities').done(function(response) { 1582 if (response.code == 200) { 1583 response.items.forEach(function(city) { 1584 $('#citySelectBusiness').append(`<option value="${city.id}">${city.name}</option>`); 1585 }); 1586 } else { 1587 t('e', response.msg); 1588 } 1589 }).fail(function(jqXHR, textStatus, errorThrown) { 1590 ajax_error(jqXHR); 1591 }); 1592}); 1593 1594// Handle state change for personal address 1595$('#stateSelectPersonal').change(function() { 1596 var stateId = $(this).val(); 1597 $('#citySelectPersonal').empty().append('<option value="" selected>Select City</option>'); 1598 1599 ajax_form({state:stateId}, 'https://haroct.com.my/api/v1/g/get-location-cities').done(function(response) { 1600 if (response.code == 200) { 1601 response.items.forEach(function(city) { 1602 $('#citySelectPersonal').append(`<option value="${city.id}">${city.name}</option>`); 1603 }); 1604 } else { 1605 t('e', response.msg); 1606 } 1607 }).fail(function(jqXHR, textStatus, errorThrown) { 1608 ajax_error(jqXHR); 1609 }); 1610}); 1611 1612 1613 1614document.querySelectorAll("input[type='checkbox']").forEach(checkbox => { 1615 checkbox.addEventListener("change", function () { 1616 this.style.accentColor = "#5C2D91"; 1617 }); 1618}); 1619 1620document.addEventListener("DOMContentLoaded", function () { 1621 document.querySelectorAll("form").forEach(form => { 1622 form.addEventListener("submit", function (event) { 1623 let isValid = true; 1624 1625 // Select all required fields excluding optional ones 1626 form.querySelectorAll(".required").forEach(field => { 1627 if (!field.value.trim()) { 1628 isValid = false; 1629 field.classList.add("border", "border-danger"); 1630 1631 // Add error message if not exists 1632 if (!field.nextElementSibling || !field.nextElementSibling.classList.contains("error-text")) { 1633 let errorText = document.createElement("small"); 1634 errorText.classList.add("text-danger", "error-text");
1635 errorText.innerText = "Please fill this field."; 1636 field.parentNode.appendChild(errorText); 1637 } 1638 } 1639 }); 1640 1641 // Check required checkboxes 1642 form.querySelectorAll("input[type='checkbox'][required]").forEach(checkbox => { 1643 if (!checkbox.checked) { 1644 isValid = false; 1645 1646 if (!checkbox.nextElementSibling || !checkbox.nextElementSibling.classList.contains("error-text")) { 1647 let errorText = document.createElement("small"); 1648 errorText.classList.add("text-danger", "error-text", "d-block"); 1649 errorText.innerText = "Please check this box."; 1650 checkbox.parentNode.appendChild(errorText); 1651 } 1652 } 1653 }); 1654 1655 if (!isValid) { 1656 event.preventDefault(); // Prevent form submission if invalid 1657 } 1658 }); 1659 1660 // Remove error styling when user types/selects 1661 form.querySelectorAll(".required").forEach(field => { 1662 field.addEventListener("input", function () { 1663 this.classList.remove("border", "border-danger"); 1664 if (this.nextElementSibling && this.nextElementSibling.classList.contains("error-text")) { 1665 this.nextElementSibling.remove(); 1666 } 1667 }); 1668 }); 1669 1670 form.querySelectorAll("input[type='checkbox'][required]").forEach(checkbox => { 1671 checkbox.addEventListener("change", function () { 1672 if (this.nextElementSibling && this.nextElementSibling.classList.contains("error-text")) { 1673 this.nextElementSibling.remove(); 1674 } 1675 }); 1676 }); 1677 }); 1678}); 1679 1680 $(document).on('submit', '#companyAddressForm, #addressForm', function (e) { 1681 e.preventDefault(); // Prevent form submission 1682 1683 // Get the address data 1684 const address1 = $('#address1').val(); 1685 const address2 = $('#address2').val(); 1686 const stateText = $(this).find('#stateSelectBusiness option:selected, #stateSelectPersonal option:selected').text(); 1687 const cityText = $(this).find('#citySelectBusiness option:selected, #citySelectPersonal option:selected').text(); 1688 const postcode = $('#postcode').val(); 1689 1690 // Construct the full address 1691 const addressParts = [address1, address2, postcode, cityText, stateText ].filter(part => part && part.trim() !== ''); 1692 const fullAddress = addressParts.join(', '); 1693 1694 // Update the address display in the container 1695 if ($(this).attr('id') === 'companyAddressForm') { 1696 $('#businessAddressDisplay').text(fullAddress); 1697 $('#businessAddress').val(fullAddress); // Store the full address in a hidden field 1698 } else { 1699 $('#personalAddressDisplay').text(fullAddress); 1700 $('#personalAddress').val(fullAddress); // Store the full address in a hidden field 1701 } 1702 1703 // Close the modal 1704 $(this).closest('.modal').modal('hide'); 1705 }); 1706 1707 $(document).on('submit', '#signupForm', function(e) { 1708 e.preventDefault(); 1709 $('[type="submit"]').prop('disabled', true); 1710 1711 // Get common form data (from steps 1-3) 1712 const commonData = { 1713 phone: $('[name="phone"]').val(), 1714 countryCode: '+' + iti.getSelectedCountryData().dialCode, 1715 otp: $('[name="otp"]').val(), 1716 code: $('[name="code"]').val(), 1717 accountType: $('[name="accountType"]:checked').val(), 1718 accountSegment: $('[name="accountSegment"]:checked').val(), 1719 verticalType: $('[name="verticalType"]:checked').val(), 1720 businessCountryId: $('[name="businessCountryId"]:checked').val(), 1721 interestedMarketIds: $('[name="interestedMarketIds[]"]:checked').map(function() { 1722 return $(this).val(); 1723 }).get(), 1724 version: 1000, 1725 accessToken: '', 1726 userId: '0', 1727 password: getPinValue() 1728 }; 1729 1730 // Add referral code if exists 1731 if ($('.referral-block span').text().length) { 1732 commonData.refCode = $('.referral-block span').text(); 1733 } 1734 1735 // Include parentId if present 1736 const parentIdVal = $('[name="parentId"]').val(); 1737 if (parentIdVal) commonData.parentId = parentIdVal; 1738 1739 const selectedCategoryIds = $('.agri-product-checkbox:checked').map(function() { 1740 return $(this).val(); 1741 }).get(); 1742 if (selectedCategoryIds.length > 0) { 1743 commonData['categoryIds'] = selectedCategoryIds; 1744 } 1745 1746 // Determine if we're submitting the normal form or agri input form 1747 const isAgriInputForm = $('#step4 .agri-input-form').is(':visible'); 1748 1749 if (isAgriInputForm) { 1750 // Handle Agri Input Form submission (business or personal) 1751 const isBusinessForm = $('#business').hasClass('active'); 1752 1753 if (isBusinessForm) { 1754 // Business form data 1755 const businessData = { 1756 store: { 1757 name: $('#businessName').val(), 1758 address1: $('#companyAddress1').val(), 1759 address2: $('#companyAddress2').val(), 1760 state: $('#stateSelectBusiness').val(), 1761 city: $('#citySelectBusiness').val(), 1762 postcode: $('#postcode').val(), 1763 phone: $('#personInChargeContact').val(), 1764 countryCode: '+' + itipersonInChargeContact.getSelectedCountryData().dialCode, 1765 email: $('#businessEmail').val(), 1766 ssmNumber: $('#businessRegNo').val() 1767 }, 1768 name: $('#personInChargeName').val(), 1769 lat: $('#latCompany').val(), 1770 lng: $('#lngCompany').val(), 1771 verticalType: "agri-input", 1772 isBusiness: 1, 1773 password: getPinValue() 1774 }; 1775
1776 // Merge common data with business data 1777 const formData = {...commonData, ...businessData}; 1778 1779 // Validate required fields 1780 let isValid = true; 1781 $('#business .required').each(function() { 1782 if ($(this).is(':visible') && 1783 ((!$(this).val() && !$(this).is(':checkbox')) || 1784 ($(this).is(':checkbox') && !$(this).is(':checked')))) { 1785 isValid = false; 1786 $(this).addClass('border border-danger'); 1787 // Scroll to first error 1788 if (isValid) { 1789 $('html, body').animate({ 1790 scrollTop: $(this).offset().top - 100 1791 }, 500); 1792 } 1793 } 1794 }); 1795 1796 if (!isValid) { 1797 t('e', 'Please fill in all required fields'); 1798 $('[type="submit"]').prop('disabled', false); 1799 return false; 1800 } 1801 1802 // Submit the form 1803 ajax_form(formData, 'https://haroct.com.my/api/v1/u/signup').done(function(response) { 1804 handleSignupResponse(response); 1805 }).fail(function(jqXHR, textStatus, errorThrown) { 1806 ajax_error(jqXHR); 1807 $('[type="submit"]').prop('disabled', false); 1808 }); 1809 1810 } else { 1811 // Personal form data 1812 const personalData = { 1813 name: $('#personalName').val(), 1814 email: $('#personalEmail').val(), 1815 idNo: $('#personalIC').val(), 1816 lat: $('#lat').val(), 1817 lng: $('#lng').val(), 1818 delivery: { 1819 name: $('#nameAddress').val(), 1820 phone: $('#contact').val(), 1821 countryCode: '+' + itiContact.getSelectedCountryData().dialCode, 1822 address: $('#address1').val(), 1823 addressLine2: $('#address2').val(), 1824 locationState: $('#stateSelectPersonal').val(), 1825 locationCity: $('#citySelectPersonal').val(), 1826 postcode: $('#personalPostcode').val() 1827 }, 1828 verticalType: "agri-input", 1829 isBusiness: 0, 1830 password: getPinValue() 1831 }; 1832 1833 // Merge common data with personal data 1834 const formData = {...commonData, ...personalData}; 1835 1836 const idDocEncoded = $('[name="idDoc[data]"]').val(); 1837 if (idDocEncoded) { 1838 formData['idDoc[data]'] = idDocEncoded; 1839 } 1840 1841 // Validate required fields 1842 let isValid = true; 1843 $('#personal .required').each(function() { 1844 if ($(this).is(':visible') && 1845 ((!$(this).val() && !$(this).is(':checkbox')) || 1846 ($(this).is(':checkbox') && !$(this).is(':checked')))) { 1847 isValid = false; 1848 $(this).addClass('border border-danger'); 1849 // Scroll to first error 1850 if (isValid) { 1851 $('html, body').animate({ 1852 scrollTop: $(this).offset().top - 100 1853 }, 500); 1854 } 1855 } 1856 }); 1857 1858 if (!isValid) { 1859 t('e', 'Please fill in all required fields'); 1860 $('[type="submit"]').prop('disabled', false); 1861 return false; 1862 } 1863 1864 ajax_form(formData, 'https://haroct.com.my/api/v1/u/signup').done(function(response) { 1865 handleSignupResponse(response); 1866 }).fail(function(jqXHR, textStatus, errorThrown) { 1867 ajax_error(jqXHR); 1868 $('[type="submit"]').prop('disabled', false); 1869 }); 1870 } 1871 } else { 1872 commonData.locationState = $('#stateSelect').val(); 1873 commonData.locationCity = $('#citySelect').val(); 1874 1875 commonData.name = $('#step5 [name="name"]:visible').val(); 1876 commonData.email = $('#step5 [name="email"]:visible').val(); 1877 if($('#step5 [name="isBusiness"]').val()){ 1878 commonData.isBusiness = 1; 1879 commonData.accountType = 1; 1880 } 1881 commonData.avatar = avatar; 1882 1883 // Validate required fields 1884 let isValid = true; 1885 $('.normal-form .required').each(function() { 1886 if ((!$(this).val() && !$(this).is(':checkbox')) || 1887 ($(this).is(':checkbox') && !$(this).is(':checked'))) { 1888 isValid = false;
1889 $(this).addClass('border', 'border-danger'); 1890 // Scroll to first error 1891 if (isValid) { 1892 $('html, body').animate({ 1893 scrollTop: $(this).offset().top - 100 1894 }, 500); 1895 } 1896 } 1897 }); 1898 1899 if (!isValid) { 1900 t('e', 'Please fill in all required fields'); 1901 $('[type="submit"]').prop('disabled', false); 1902 return false; 1903 } 1904 1905 // Check if email is filled 1906 const email = commonData.email; 1907 1908 if (!email || email.trim() === '') { 1909 // Email is empty, show the modal 1910 emailModal.show(); 1911 $('[type="submit"]').prop('disabled', false); 1912 1913 // Store the form data for later use 1914 $('#signupForm').data('pendingSubmission', commonData); 1915 } else { 1916 // Email is filled, proceed with signup 1917 submitNormalForm(commonData); 1918 } 1919 } 1920}); 1921 1922function submitNormalForm(formData) { 1923 ajax_form(formData, 'https://haroct.com.my/api/v1/u/signup').done(function(response) { 1924 handleSignupResponse(response); 1925 }).fail(function(jqXHR, textStatus, errorThrown) { 1926 ajax_error(jqXHR); 1927 $('[type="submit"]').prop('disabled', false); 1928 }); 1929} 1930 1931// Add this at the end of the document ready function to handle modal button clicks 1932$(document).on('click', '#emailModal .btn-haroct', function(e) { 1933 e.preventDefault(); 1934 1935 // User clicked "Add Email Now" - close modal 1936 emailModal.hide(); 1937 $('[type="submit"]').prop('disabled', false); 1938}); 1939 1940$(document).on('click', '#emailModal a[data-bs-dismiss="modal"]', function(e) { 1941 e.preventDefault(); 1942 1943 // User clicked "It's okay, I will do it later" - proceed with signup 1944 const pendingData = $('#signupForm').data('pendingSubmission'); 1945 if (pendingData) { 1946 emailModal.hide(); 1947 submitNormalForm(pendingData); 1948 // Clear the stored data 1949 $('#signupForm').removeData('pendingSubmission'); 1950 } 1951}); 1952 1953// Initialize the modal 1954var emailModal = new bootstrap.Modal(document.getElementById('emailModal')); 1955 1956function handleSignupResponse(response) { 1957 if (response.code == 200) { 1958 t('s', response.msg); 1959 const accountSegment = $('[name="accountSegment"]:checked').val(); 1960 if (accountSegment == 106 || accountSegment == 205) { 1961 location.href = 'https://haroct.com.my/agri-input/auth?accessToken=' + response.accessToken; 1962 } else { 1963 location.href = 'https://haroct.com.my/store/auth?accessToken=' + response.accessToken; 1964 } 1965 } else { 1966 t('e', response.msg); 1967 $('[type="submit"]').prop('disabled', false); 1968 } 1969} 1970 1971 /* 1972 $(document).on('click', '.btn-submit-referral', function(e){ 1973 e.preventDefault(); 1974 code = $('#referral-code').val().replace(/[^a-zA-Z0-9]/g, ''); 1975 if(code.length){ 1976 $('.btn-claim').val('Claimed'); 1977 $('.referral-block span').text(code); 1978 $('.referral-block').removeClass('d-none'); 1979 }else{ 1980 $('.btn-claim').val('Claim'); 1981 $('.referral-block span').text(''); 1982 $('.referral-block').addClass('d-none'); 1983 } 1984 }); 1985 */ 1986 1987 $(document).on('click', '.btn-remove-referral', function(e){ 1988 e.preventDefault(); 1989 $('.btn-claim').val('Claim'); 1990 $('#referral-code').val(''); 1991 $('.referral-block span').text(''); 1992 $('.referral-block').addClass('d-none'); 1993 }); 1994 1995 $('[name="accountType"]').change(); 1996}); 1997 1998var iti; 1999var itiContact; 2000var itipersonInChargeContact; 2001var rtoken = ''; 2002 2003$(function(){ 2004 $(document).on('click', 'body', function(){ 2005 grecaptcha.enterprise.ready(async () => { 2006 rtoken = await grecaptcha.enterprise.execute('6Lcim1osAAAAANvZ5krlNRs6QP11OT71fvoj2ous', {action: 'LOGIN'}); 2007 }); 2008 }); 2009 2010 iti = window.intlTelInput(document.querySelector("#phone"), { 2011 initialCountry: "my", 2012 onlyCountries: ["my", "th", "sg"], 2013 showDialCode: true, 2014 separateDialCode: true, 2015 }); 2016 2017 document.querySelector("#phone").addEventListener("countrychange", function() { 2018 var selectedCountry = iti.getSelectedCountryData(); 2019 var countryCode = '+' + selectedCountry.dialCode; 2020 $('[name="countryCode"]').val(countryCode); 2021 2022 if (selectedCountry.iso2 === 'th') { 2023 $('.thai-guide').removeClass('d-none'); 2024 } else if (selectedCountry.iso2 === 'my') { 2025 $('.thai-guide').addClass('d-none'); 2026 } 2027 }); 2028 2029 document.querySelector("#phone").addEventListener("input", function() { 2030 var selectedCountry = iti.getSelectedCountryData().iso2; 2031 2032 if (selectedCountry === 'th') { 2033 var phoneValue = this.value; 2034 2035 if (phoneValue.startsWith('0')) { 2036 $('.default-thai-guide').addClass('d-none'); 2037 $('.error-thai-guide').removeClass('d-none'); 2038 $('.signup-btn').addClass('disabled'); 2039 } else { 2040 $('.default-thai-guide').removeClass('d-none'); 2041 $('.error-thai-guide').addClass('d-none'); 2042 $('.signup-btn').removeClass('disabled'); 2043 } 2044 } 2045 }); 2046}); 2047 2048 $(function () { 2049 // Initialize intlTelInput for #contact 2050 itiContact = window.intlTelInput(document.querySelector("#contact"), { 2051 initialCountry: "my", 2052 onlyCountries: ["my", "th", "sg"],
2053 showDialCode: true, 2054 separateDialCode: true 2055 }); 2056 2057 // Listen for country change events 2058 document.querySelector("#contact").addEventListener("countrychange", function () { 2059 var selectedCountry = itiContact.getSelectedCountryData(); 2060 var countryCode = '+' + selectedCountry.dialCode; 2061 $('#countryCode').val(countryCode); 2062 2063 // Show/hide Thai-specific guidance 2064 if (selectedCountry.iso2 === 'th') { 2065 $('.thai-guide').removeClass('d-none'); 2066 } else { 2067 $('.thai-guide').addClass('d-none'); 2068 } 2069 }); 2070 2071 // Validate Thai phone numbers 2072 document.querySelector("#contact").addEventListener("input", function () { 2073 var selectedCountry = itiContact.getSelectedCountryData().iso2; 2074 var phoneValue = this.value.trim(); 2075 2076 if (selectedCountry === 'th') { 2077 if (phoneValue.startsWith('0')) { 2078 $('.default-thai-guide').addClass('d-none'); 2079 $('.error-thai-guide').removeClass('d-none'); 2080 $('.btn-haroct').addClass('disabled'); 2081 } else { 2082 $('.default-thai-guide').removeClass('d-none'); 2083 $('.error-thai-guide').addClass('d-none'); 2084 $('.btn-haroct').removeClass('disabled'); 2085 } 2086 } 2087 }); 2088 }); 2089 2090 2091 $(function () { 2092 // Initialize intlTelInput for #contact 2093 itipersonInChargeContact = window.intlTelInput(document.querySelector("#personInChargeContact"), { 2094 initialCountry: "my", 2095 onlyCountries: ["my", "th", "sg"], 2096 showDialCode: true, 2097 separateDialCode: true 2098 }); 2099 2100 // Listen for country change events 2101 document.querySelector("#personInChargeContact").addEventListener("countrychange", function () { 2102 var selectedCountry = itipersonInChargeContact.getSelectedCountryData(); 2103 var countryCode = '+' + selectedCountry.dialCode; 2104 $('#countryCode').val(countryCode); 2105 2106 // Show/hide Thai-specific guidance 2107 if (selectedCountry.iso2 === 'th') { 2108 $('.thai-guide').removeClass('d-none'); 2109 } else { 2110 $('.thai-guide').addClass('d-none'); 2111 } 2112 }); 2113 2114 // Validate Thai phone numbers 2115 document.querySelector("#personInChargeContact").addEventListener("input", function () { 2116 var selectedCountry = itipersonInChargeContact.getSelectedCountryData().iso2; 2117 var phoneValue = this.value.trim(); 2118 2119 if (selectedCountry === 'th') { 2120 if (phoneValue.startsWith('0')) { 2121 $('.default-thai-guide').addClass('d-none'); 2122 $('.error-thai-guide').removeClass('d-none'); 2123 $('.btn-haroct').addClass('disabled'); 2124 } else { 2125 $('.default-thai-guide').removeClass('d-none'); 2126 $('.error-thai-guide').addClass('d-none'); 2127 $('.btn-haroct').removeClass('disabled'); 2128 } 2129 } 2130 }); 2131 }); 2132 2133 2134let otpCountdownInterval; 2135function startOtpCountdown() { 2136 let timeLeft = 5 * 60 - 1; 2137 $('.otp-countdown').removeClass('d-none'); 2138 $('.btn-resend-otp').addClass('d-none'); 2139 2140 if (otpCountdownInterval) clearInterval(otpCountdownInterval); 2141 2142 otpCountdownInterval = setInterval(function() { 2143 const minutes = Math.floor(timeLeft / 60); 2144 const seconds = timeLeft % 60; 2145 $('.otp-countdown span').text(`${minutes}:${seconds.toString().padStart(2, '0')}`); 2146 2147 if (timeLeft <= 0) { 2148 clearInterval(otpCountdownInterval); 2149 $('.otp-countdown span').text(''); 2150 $('.otp-countdown').addClass('d-none'); 2151 $('.btn-resend-otp').removeClass('d-none'); 2152 } 2153 timeLeft--; 2154 }, 1000); 2155} 2156 2157$(document).on('click', '.btn-resend-otp', function(e) { 2158 e.preventDefault(); 2159 $('.btn-request-otp').click(); 2160}); 2161 2162 $(document).on('click', '.btn-request-otp', function(e){ 2163 e.preventDefault(); 2164 let el = $(this); 2165 2166 // Check if T&C is checked 2167 if (!$('#tnc').is(':checked')) { 2168 t('e', 'Please accept the Terms & Conditions'); 2169 return false;
2170 } 2171 2172 let originalText = el.text(); 2173 let countryCode = '+' + iti.getSelectedCountryData().dialCode; 2174 let phone = $('[name="phone"]').val().replace(/[^a-zA-Z0-9]/g, ''); 2175 2176 $('[name="countryCode"]').val(countryCode); 2177 2178 if(!phone.length){ 2179 t('e', 'Phone number is required'); 2180 return false; 2181 } 2182 2183 // Skip OTP for Thailand (+66) 2184 if (countryCode === '+66') { 2185 if (typeof window.proceedToStep2 === 'function') { 2186 window.proceedToStep2(); 2187 } 2188 return false; 2189 } 2190 2191 $('.otp-pin').val(''); 2192 $('[name="otp"]').val(''); 2193 $('[name="code"]').val(''); 2194 checkOtpComplete(); 2195 2196 let data = {phone: phone, countryCode: countryCode, rtoken: rtoken, version: 1000}; 2197 el.prop('disabled', true).text('Requesting...'); 2198 2199 $('.phone-number').text(countryCode + phone); 2200 2201 ajax_form(data, 'https://haroct.com.my/api/v1/u/check-phone-availability').done(function(response) { 2202 if (response.code == 200) { 2203 ajax_form(data, 'https://haroct.com.my/api/v1/u/request-otp').done(function(response) { 2204 if (response.code == 200) { 2205 // t('s', response.msg); 2206 2207 var otpModal = new bootstrap.Modal(document.getElementById('otpModal')); 2208 otpModal.show(); 2209 2210 startOtpCountdown(); 2211 2212 setTimeout(function() { 2213 $('.otp-pin').first().focus(); 2214 }, 500); 2215 2216 } else { 2217 t('e', response.msg); 2218 } 2219 }).fail(function(jqXHR, textStatus, errorThrown) { 2220 ajax_error(jqXHR); 2221 }).always(function() { 2222 el.prop('disabled', false).text(originalText); 2223 }); 2224 }else{ 2225 t('e', response.msg); 2226 el.prop('disabled', false).text(originalText); 2227 } 2228 }).fail(function(jqXHR, textStatus, errorThrown) { 2229 ajax_error(jqXHR); 2230 el.prop('disabled', false).text(originalText); 2231 }); 2232}); 2233 2234$(document).on('click', '.btn-verify-otp', function() { 2235 let otpValue = ''; 2236 $('.otp-pin').each(function() { 2237 otpValue += $(this).val(); 2238 }); 2239 2240 let phone = $('[name="phone"]').val().replace(/[^a-zA-Z0-9]/g, ''); 2241 let countryCode = '+' + iti.getSelectedCountryData().dialCode; 2242 2243 $('[name="otp"]').val(otpValue); 2244 $('[name="code"]').val(otpValue); 2245 2246 let verifyBtn = $(this); 2247 let originalText = verifyBtn.text(); 2248 verifyBtn.prop('disabled', true).text('Verifying...'); 2249 2250 ajax_form({ 2251 phone: phone, 2252 countryCode: countryCode, 2253 otp: otpValue 2254 }, 'https://haroct.com.my/api/v1/u/validate-otp').done(function(response) { 2255 if (response.code == 200) { 2256 var otpModal = bootstrap.Modal.getInstance(document.getElementById('otpModal')); 2257 if(otpModal) otpModal.hide(); 2258 2259 if (typeof window.proceedToStep2 === 'function') { 2260 window.proceedToStep2(); 2261 } 2262 } else { 2263 t('e', response.msg || 'Invalid OTP code'); 2264 $('.otp-pin').val(''); 2265 $('.otp-pin').first().focus(); 2266 verifyBtn.prop('disabled', true); 2267 } 2268 }).fail(function(jqXHR, textStatus, errorThrown) { 2269 ajax_error(jqXHR); 2270 }).always(function() { 2271 verifyBtn.prop('disabled', false).text(originalText); 2272 }); 2273}); 2274 2275function checkOtpComplete() { 2276 const otpInputs = $('.otp-pin'); 2277 const isComplete = otpInputs.toArray().every(input => $(input).val()); 2278 $('.btn-verify-otp').prop('disabled', !isComplete); 2279} 2280 2281$(document).on('input', '.otp-pin', function (e) { 2282 let index = $('.otp-pin').index(this); 2283 2284 if (this.value.length > 1) { 2285 let pastedData = this.value.substring(0, 6); 2286 $('.otp-pin').each(function(i, el) { 2287 if (i < pastedData.length) { 2288 $(el).val(pastedData[i]); 2289 } else { 2290 $(el).val(''); 2291 } 2292 }); 2293 if (pastedData.length === 6) { 2294 $('.otp-pin').last().focus(); 2295 } else { 2296 $('.otp-pin').eq(pastedData.length).focus(); 2297 } 2298 checkOtpComplete(); 2299 return; 2300 } 2301 2302 const value = this.value; 2303 if (value.length === 1 && index < $('.otp-pin').length - 1) { 2304 $('.otp-pin').eq(index + 1).focus(); 2305 } 2306 if (value.length === 0 && index > 0) { 2307 $('.otp-pin').eq(index - 1).focus(); 2308 } 2309 checkOtpComplete(); 2310}); 2311 2312$(document).on('keydown', '.otp-pin', function (e) { 2313 let index = $('.otp-pin').index(this); 2314 if (e.key === 'Backspace' && !this.value && index > 0) { 2315 $('.otp-pin').eq(index - 1).focus(); 2316 } 2317}); 2318 2319 2320var referralModal = new bootstrap.Modal(document.getElementById('referralModal')); 2321$(document).on('click', '.btn-submit-referral', function(e){ 2322 e.preventDefault(); 2323 code = $('#referral-code').val().replace(/[^a-zA-Z0-9]/g, ''); 2324 if(code.length){ 2325 ajax_form({refCode: code, version: 1000}, 'https://haroct.com.my/api/v1/u/check-ref-code').done(function(response) { 2326 if (response.code == 200) { 2327 referralModal.hide(); 2328 2329 $('.btn-claim').val('Claimed'); 2330 $('.referral-block span').text(code); 2331 $('.referral-block').removeClass('d-none'); 2332 } else { 2333 t('e', response.msg); 2334 } 2335 }).fail(function(jqXHR, textStatus, errorThrown) { 2336 ajax_error(jqXHR); 2337 }).always(function(){ 2338 $('[type="submit"]').prop('disabled', false); 2339 }); 2340 }else{ 2341 $('.btn-claim').val('Claim'); 2342 $('.referral-block span').text(''); 2343 $('.referral-block').addClass('d-none'); 2344 } 2345}); 2346 2347$(document).on('click', '.btn-remove-referral', function(e){ 2348 e.preventDefault(); 2349 $('.btn-claim').val('Claim'); 2350 $('#referral-code').val(''); 2351 $('.referral-block span').text(''); 2352 $('.referral-block').addClass('d-none'); 2353}); 2354function jsonToUrlParams(json) { 2355 return Object.keys(json) 2356 .map(key => encodeURIComponent(key) + '=' + encodeURIComponent(json[key])) 2357 .join('&'); 2358} 2359</script>
2359 2360 2361
2362<script> 2363var mapCompany, mapPersonal, markerCompany, markerPersonal, autocompleteCompany, autocompletePersonal; 2364const defaultLocation = { lat: 3.1390, lng: 101.6869 }; 2365 2366function initAutocomplete() { 2367// console.log('Initializing autocomplete...'); 2368 if ("geolocation" in navigator) { 2369 navigator.geolocation.getCurrentPosition(function(position) { 2370 const userLocation = { 2371 lat: position.coords.latitude, 2372 lng: position.coords.longitude 2373 }; 2374 initializeMap(userLocation, 'company'); 2375 initializeMap(userLocation, 'personal'); 2376 }, function(error) { 2377 console.error("Error getting user location:", error); 2378 initializeMap(defaultLocation, 'company'); 2379 initializeMap(defaultLocation, 'personal'); 2380 }); 2381 } else { 2382 // console.log("Geolocation is not supported by this browser."); 2383 initializeMap(defaultLocation, 'company'); 2384 initializeMap(defaultLocation, 'personal'); 2385 } 2386 2387 // Initialize autocomplete for company address 2388 autocompleteCompany = new google.maps.places.Autocomplete( 2389 document.getElementById('companyAddress2'), 2390 { types: ['geocode', 'establishment'], componentRestrictions: { country: 'my' } } 2391 ); 2392 autocompleteCompany.addListener('place_changed', function () { 2393 handlePlaceChanged(autocompleteCompany, 'company'); 2394 }); 2395 2396 // Initialize autocomplete for personal address 2397 autocompletePersonal = new google.maps.places.Autocomplete( 2398 document.getElementById('address2'), 2399 { types: ['geocode', 'establishment'], componentRestrictions: { country: 'my' } } 2400 ); 2401 autocompletePersonal.addListener('place_changed', function () { 2402 handlePlaceChanged(autocompletePersonal, 'personal'); 2403 }); 2404} 2405 2406function handlePlaceChanged(autocomplete, type) { 2407 const place = autocomplete.getPlace(); 2408 if (place.geometry) { 2409 const map = type === 'company' ? mapCompany : mapPersonal; 2410 const marker = type === 'company' ? markerCompany : markerPersonal; 2411 2412 map.setCenter(place.geometry.location); 2413 map.setZoom(13); 2414 2415 marker.setPosition(place.geometry.location); 2416 let placeName = ''; 2417 if (place.name) { 2418 placeName = place.name.split(',')[0].trim(); 2419 } else { 2420 place.address_components.some(component => { 2421 if (component.types.includes('premise') || component.types.includes('point_of_interest') || component.types.includes('establishment')) { 2422 placeName = component.long_name; 2423 return true; 2424 } 2425 }); 2426 } 2427 if (!placeName && place.formatted_address) { 2428 placeName = place.formatted_address.split(',')[0].trim(); 2429 } 2430 2431 document.getElementById(type === 'company' ? 'companyAddress2' : 'address2').value = placeName; 2432 document.getElementById(type === 'company' ? 'latCompany' : 'lat').value = place.geometry.location.lat(); 2433 document.getElementById(type === 'company' ? 'lngCompany' : 'lng').value = place.geometry.location.lng(); 2434 } 2435} 2436 2437function initializeMap(location, type) { 2438 const mapElement = type === 'company' ? document.querySelector(".map-container-company") : document.querySelector(".map-container-personal"); 2439 const map = new google.maps.Map(mapElement, { 2440 center: location, 2441 zoom: 13 2442 }); 2443 2444 const marker = new google.maps.Marker({ 2445 position: location, 2446 map: map, 2447 draggable: true 2448 }); 2449 2450 google.maps.event.addListener(marker, 'dragend', function () { 2451 const position = marker.getPosition(); 2452 document.getElementById(type === 'company' ? 'latCompany' : 'lat').value = position.lat(); 2453 document.getElementById(type === 'company' ? 'lngCompany' : 'lng').value = position.lng(); 2454 }); 2455 2456 document.getElementById(type === 'company' ? 'latCompany' : 'lat').value = location.lat; 2457 document.getElementById(type === 'company' ? 'lngCompany' : 'lng').value = location.lng; 2458 2459 if (type === 'company') { 2460 mapCompany = map; 2461 markerCompany = marker; 2462 } else { 2463 mapPersonal = map; 2464 markerPersonal = marker; 2465 } 2466} 2467 2468window.onload = initAutocomplete; 2469 2470let currentPage = 1; 2471let isLoading = false;
2472let hasMoreItems = true; 2473let searchTimeout = null; 2474 2475function getProductName(page = 1, keyword = '') { 2476 const requestData = { 2477 page: page, 2478 search: keyword, 2479 parent: 820 2480 }; 2481 2482 // Send AJAX request using ajax_form 2483 ajax_form(requestData, 'https://haroct.com.my/api/v1/products/get-categories').done(function(response) { 2484 if (response.code == 200 && response.items) { 2485 2486 if (page === 1) { 2487 document.querySelector('.product-list-container').innerHTML = ''; 2488 } 2489 2490 // Append product name options to the container 2491 appendProductNameOptions(response.items, '.product-list-container'); 2492 2493 // Update hasMoreItems based on response 2494 hasMoreItems = response.items.length > 0; 2495 } 2496 isLoading = false; 2497 }).fail(function(jqXHR, textStatus, errorThrown) { 2498 // Handle AJAX errors 2499 console.error('AJAX request failed:', textStatus, errorThrown); 2500 isLoading = false; 2501 }); 2502} 2503 2504 2505function appendProductNameOptions(items, selector) { 2506 const productListContainer = document.querySelector(selector); 2507 2508 items.forEach(item => { 2509 const productDiv = document.createElement('div'); 2510 productDiv.className = 'product-item'; 2511 productDiv.setAttribute('data-product-id', item.id); 2512 productDiv.setAttribute('data-product-name', item.name); 2513 2514 const checkbox = document.createElement('input'); 2515 checkbox.type = 'checkbox'; 2516 checkbox.className = 'product-checkbox'; 2517 checkbox.setAttribute('data-product-id', item.id); 2518 checkbox.setAttribute('data-product-name', item.name); 2519 2520 const label = document.createElement('label'); 2521 label.textContent = item.name; 2522 label.className = 'product-label'; 2523 2524 productDiv.appendChild(checkbox); 2525 productDiv.appendChild(label); 2526 productListContainer.appendChild(productDiv); 2527 }); 2528} 2529 2530$(function() { 2531 productNameModal = new bootstrap.Modal(document.getElementById('productNameModal')); 2532 2533 // Product name click handler 2534 $(document).on('click', '.product-name', function(e) { 2535 e.preventDefault(); 2536 currentPage = 1; 2537 hasMoreItems = true; 2538 getProductName(currentPage); 2539 productNameModal.show(); 2540 }); 2541 2542 // Product item selection handler 2543 $(document).on('change', '.product-checkbox', function() { 2544 const selectedProducts = []; 2545 $('.product-checkbox:checked').each(function() { 2546 selectedProducts.push($(this).attr('data-product-name')); 2547 }); 2548 $('.product-name-label').html(selectedProducts.join(', ')); 2549 }); 2550 2551 // Scroll pagination handler 2552 $('.product-list').on('scroll', function() { 2553 const { scrollTop, scrollHeight, clientHeight } = this; 2554 2555 // Check if we're near the bottom (within 50px) 2556 if (scrollHeight - scrollTop - clientHeight < 50) { 2557 if (!isLoading && hasMoreItems) { 2558 isLoading = true; 2559 currentPage++; 2560 2561 // Get current search keyword 2562 const keyword = $('.search-input').val().trim(); 2563 getProductName(currentPage, keyword); 2564 } 2565 } 2566 }); 2567 2568 // Confirm button handler 2569 $('.confirm-product-name').on('click', function(e) { 2570 e.preventDefault(); 2571 productNameModal.hide(); 2572 }); 2573 2574 $('.search-input').on('input', function() { 2575 const keyword = $(this).val().trim(); 2576 2577 // Clear any existing timeout 2578 if (searchTimeout) { 2579 clearTimeout(searchTimeout); 2580 } 2581 2582 // Reset pagination variables 2583 currentPage = 1; 2584 hasMoreItems = true; 2585 isLoading = false; 2586 2587 // Set new timeout for search 2588 searchTimeout = setTimeout(() => { 2589 getProductName(currentPage, keyword); 2590 }, 300); 2591 }); 2592 2593 const $fileInput = $('#storeLogoInput'); 2594 const $uploadButton = $('.upload-btn'); 2595 const $logoPlaceholder = $('#logo-placeholder'); 2596 2597 function triggerFileInput() { 2598 $fileInput.trigger('click'); 2599 } 2600 2601 $uploadButton.on('click', triggerFileInput); 2602 $logoPlaceholder.on('click', triggerFileInput); 2603 2604 $fileInput.on('change', function(event) { 2605 const file = event.target.files[0]; 2606 2607 if (file) { 2608 const reader = new FileReader(); 2609 2610 reader.onload = function(e) { 2611 const base64String = e.target.result; 2612 avatar = base64String; 2613 $logoPlaceholder.empty(); 2614 2615 const $img = $('<img>', { 2616 src: base64String, 2617 class: 'img-fluid rounded-circle' 2618 }); 2619 $logoPlaceholder.append($img); 2620 }; 2621
2622 reader.readAsDataURL(file); 2623 } 2624 }); 2625}); 2626 2627function getPinValue() { 2628 const pinInputs = $('.pin'); 2629 let pinValue = ''; 2630 2631 pinInputs.each(function() { 2632 pinValue += $(this).val() || '0'; 2633 }); 2634 2635 return pinValue.padStart(6, '0'); 2636} 2637 2638function checkPinComplete() { 2639 const pinInputs = $('.pin'); 2640 const isComplete = pinInputs.toArray().every(input => $(input).val()); 2641 pinInputs.closest('.form-step').find('.next-btn').prop('disabled', !isComplete); 2642} 2643</script>
2643 2644 </div> 2645 </body> 2646</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.