1import{u as p,a as f,b as g,c as m,d as y}from"./form-server-BCwInACg.js";const k="/api/i/booking/v2.0",S=60,u={ru:"ru_RU",en:"en_US",uz:"uz_LA"},_=()=>({...y(),...m(),...g(),...f(),...p(),captchaToken:null,turnstileWidgetId:null,isWaitingForCaptcha:!1,i18n:{},apiLocale:"ru_RU",availableFilials:[],filialsLoading:!1,filialsError:null,selectedFilialName:"",selectedFilialAddress:"",availableTasks:[],tasksLoading:!1,tasksError:null,selectedTaskName:"",taskDropdownOpen:!1,taskDropdownSearch:"",availableDates:[],datesLoading:!1,datesError:null,datesLoadedForStep:null,availableTimes:[],timesLoading:!1,timesError:null,otpSessionKey:null,otpExpiresAt:null,isSendingOtp:!1,isVerifyingOtp:!1,otpError:null,otpResendSeconds:0,otpResendInterval:null,bookingKey:null,bookingNumber:null,getCaptchaWidget(){return this.$root?.querySelector('[x-ref="captchaWidget"]')||this.$refs.captchaWidget||null},getNativeForm(){return this.$refs.nativeForm||this.$root?.querySelector("form")||null},initTurnstile(){const e=this.getCaptchaWidget(),i=e?.dataset?.sitekey;if(!(!e||!i||typeof turnstile>"u"||this.turnstileWidgetId))try{this.turnstileWidgetId=turnstile.render(e,{sitekey:i,execution:"execute",callback:t=>{this.captchaToken=t,this.isWaitingForCaptcha=!1},"error-callback":()=>{this.isWaitingForCaptcha=!1}})}catch(t){console.warn("[BookingForm] Turnstile render failed",t)}},init(){try{const i=this.$root.querySelector("script[data-booking-i18n]");this.i18n=JSON.parse(i?.textContent||"{}")}catch(i){console.warn("[BookingForm] Failed to parse translations",i),this.i18n={}}const e=(this.$root.querySelector('[name="locale"]')?.value||"ru").toLowerCase();this.apiLocale=u[e]||u.ru,this.$nextTick(()=>{if(this.initStepper(),this.initFormData(),this.updateConditionalFields(),this.loadFilials(),typeof turnstile<"u")this.initTurnstile();else{let i=0;const t=setInterval(()=>{typeof turnstile<"u"?(clearInterval(t),this.initTurnstile()):++i>50&&clearInterval(t)},100)}})},t(e,i={}){let t=this.i18n[e]??e;
1return Object.keys(i).forEach(s=>{t=t.replace(`{${s}}`,i[s])}),t},getFieldNameByRole(e){return this.$root.querySelector(`[data-role="${e}"]`)?.querySelector("[data-field]")?.dataset.field||null},getFieldValueByRole(e){const i=this.getFieldNameByRole(e);return i?this.formData[i]??"":""},setFieldValueByRole(e,i){const t=this.getFieldNameByRole(e);t&&(this.formData[t]=i,this.clearError&&this.clearError(t))},hasSelectedFilial(){return!!this.getFieldValueByRole("filial_select")},hasSelectedDate(){return!!this.getFieldValueByRole("date_picker")},localizeApiValue(e){return typeof e=="string"?e:e&&typeof e=="object"&&(e[this.apiLocale]||e.ru_RU||Object.values(e).find(Boolean))||""},formatDateLabel(e){const i=String(e).split("-");return i.length!==3?e:`${i[2]}.${i[1]}`},formatTimeLabel(e){return String(e).slice(0,5)},normalizeTimeString(e){const i=String(e).trim();return/^\d{1,2}:\d{2}:\d{2}$/.test(i)?i:/^\d{1,2}:\d{2}$/.test(i)?`${i}:00`:i},async apiRequest(e,{method:i="GET",query:t=null,body:s=null}={}){let a=`${k}${e}`;if(t){const d=new URLSearchParams(Object.fromEntries(Object.entries(t).filter(([,c])=>c!=null&&c!==""))).toString();d&&(a+=`?${d}`)}const r=await fetch(a,{method:i,headers:{Accept:"application/json",...s?{"Content-Type":"application/json"}:{}},body:s?JSON.stringify(s):void 0});let l=null;try{l=await r.json()}catch{l=null}return{ok:r.ok,status:r.status,data:l}},extractErrorMessage(e,i="generic_error"){if(e?.errors&&typeof e.errors=="object"){const t=Object.values(e.errors)[0],s=Array.isArray(t)?t[0]:t;if(s)return String(s)}if(typeof e?.message=="string"&&e.message)return e.message;if(e?.message&&typeof e.message=="object"){if("upstream"in e.message)return this.t("network_error");const t=Object.values(e.message).find(Boolean);if(t)return String(t)}return this.t(i)},async loadFilials(){this.filialsLoading=!0,this.filialsError=null;const{ok:e,data:i}=await this.apiRequest("/filials");e&&Array.isArray(i)?this.availableFilials=i.map(t=>({id:t.id,name:this.localizeApiValue(t.name),address:this.localizeApiValue(t.address)})):(this.availableFilials=[],this.filialsError=this.extractErrorMessage(i,"filials_error")),this.filialsLoading=!1},onFilialChange(e){const i=this.availableFilials.find(t=>t.id===e);this.selectedFilialName=i?.name||"",this.selectedFilialAddress=i?.address||"",this.setFieldValueByRole("task_select",""),this.selectedTaskName="",this.availableTasks=[],this.taskDropdownOpen=!1,this.taskDropdownSearch="",this.resetDateAndTimeSelection(),e?this.loadTasks(e):this.availableTasks=[]},async loadTasks(e){this.tasksLoading=!0,this.tasksError=null;const{ok:i,data:t}=await this.apiRequest("/tasks",{query:{filial_id:e}});i&&Array.isArray(t)?this.availableTasks=t.map(s=>({id:s.id,name:this.localizeApiValue(s.name)})):(this.availableTasks=[],this.tasksError=this.extractErrorMessage(t,"tasks_error")),this.tasksLoading=!1},onTaskChange(e){const i=this.availableTasks.find(t=>t.id===e);this.selectedTaskName=i?.name||"",this.resetDateAndTimeSelection()},resetDateAndTimeSelection(){this.setFieldValueByRole("date_picker",""),this.setFieldValueByRole("time_picker",""),this.availableDates=[],this.availableTimes=[],this.datesLoadedForStep=null},async loadCalendarAndSchedule(){const e=this.getFieldValueByRole("filial_select"),i=this.getFieldValueByRole("task_select");this.datesLoading=!0,this.datesError=null,this.timesLoading=!1,this.timesError=null;const t=await this.apiRequest("/calendar",{query:{filial_id:e,task_id:i}});t.ok?this.availableDates=this.normalizeCalendarResponse(t.data):(this.availableDates=[],this.datesError=this.extractErrorMessage(t.data,"dates_error")),this.datesLoading=!1;const s=this.getFieldValueByRole("date_picker");s&&this.onDateChange(s)},normalizeCalendarResponse(e){const i=Array.isArray(e)?e:Array.isArray(e?.dates)?e.dates:Array.isArray(e?.data)?e.data:[],t=[],s=new Set;return i.forEach(a=>{let r=null,l=!0;typeof a=="string"?r=a:a&&typeof a=="object"&&(r=a.date||a.value||a.day||null,"available"in a&&(l=!!a.available),"is_available"in a&&(l=!!a.is_available)),!(!r||!l||s.has(r))&&(s.add(r),t.push({value:r,label:this.formatDateLabel(r)}))}),t.sort((a,r)=>a.value.localeCompare(r.value))},onDateChange(e){if(!e)return;const i=this.getFieldValueByRole("filial_select"),t=this.getFieldValueByRole("task_select");return this.timesLoading=!0,this.timesError=null,this.availableTimes=[],(async()=>{try{const{ok:s,data:a}=await this.apiRequest("/calendar",{query:{filial_id:i,task_id:t,date:e}});if(!s){this.availableTimes=[],this.timesError=this.extractErrorMessage(
1a,"times_error");return}const r=Array.isArray(a)?a:Array.isArray(a?.data)?a.data:[],l=[];r.forEach(n=>{if(!n||typeof n!="object"||!n.starts_at)return;const h=this.normalizeTimeString(n.starts_at);l.push({value:h,label:this.formatTimeLabel(h),disabled:!!n.booked,meta:""})});const o=new Map;l.forEach(n=>{o.has(n.value)||o.set(n.value,n)}),this.availableTimes=Array.from(o.values()).sort((n,h)=>n.value.localeCompare(h.value));const d=this.getFieldValueByRole("time_picker"),c=this.availableTimes.find(n=>n.value===d);d&&(!c||c.disabled)&&this.setFieldValueByRole("time_picker","")}finally{this.timesLoading=!1}})()},async onBeforeNextStep(e){return e===0?(await this.loadCalendarAndSchedule(),!0):e===1?this.sendOtp():!0},buildBookingPayload(){return{filial_id:this.getFieldValueByRole("filial_select"),task_id:this.getFieldValueByRole("task_select"),date:this.getFieldValueByRole("date_picker"),starts_at:this.getFieldValueByRole("time_picker"),locale:this.apiLocale,visitor_name:this.getFieldValueByRole("visitor_name_field"),email:this.getFieldValueByRole("email_field"),filial_name:this.selectedFilialName,filial_address:this.selectedFilialAddress,task_name:this.selectedTaskName}},async sendOtp(){this.isSendingOtp=!0,this.otpError=null,this.serverError=null;const{ok:e,status:i,data:t}=await this.apiRequest("/otp/send",{method:"POST",body:this.buildBookingPayload()});return this.isSendingOtp=!1,e?(this.otpSessionKey=t.session_key,this.otpExpiresAt=t.expires_at||null,this.startOtpResendTimer(),!0):(this.serverError=this.extractErrorMessage(t,i>=500?"network_error":"otp_send_failed"),!1)},async resendOtp(){this.otpResendSeconds>0||this.isSendingOtp||await this.sendOtp()},startOtpResendTimer(){this.otpResendSeconds=S,this.otpResendInterval&&clearInterval(this.otpResendInterval),this.otpResendInterval=setInterval(()=>{if(this.otpResendSeconds<=1){clearInterval(this.otpResendInterval),this.otpResendInterval=null,this.otpResendSeconds=0;return}this.otpResendSeconds-=1},1e3)},pickOtpVerifyError(e,i){const t=i?.message;if(t&&typeof t=="object"){if("otp_code"in t)return t.otp_code==="expired"?this.t("otp_expired_code"):this.t("otp_invalid_code");if("session_key"in t)return t.session_key==="already verified"?this.t("otp_session_verified"):this.t("otp_session_unknown");if("starts_at"in t)return this.t("slot_taken");if("upstream"in t)return this.t("network_error")}return this.extractErrorMessage(i)},async confirmOtp(){this.otpError=null;const e=this.getFieldNameByRole("otp_input");if(this.touchField&&this.touchField(e),!this.validateField(e))return!1;if(!this.otpSessionKey)return this.otpError=this.t("otp_session_unknown"),!1;this.isVerifyingOtp=!0;const{ok:i,status:t,data:s}=await this.apiRequest("/otp/verify",{method:"POST",body:{session_key:this.otpSessionKey,otp_code:this.getFieldValueByRole("otp_input")}});if(this.isVerifyingOtp=!1,!i)return this.otpError=this.pickOtpVerifyError(t,s),t===409&&s?.message&&typeof s.message=="object"&&"starts_at"in s.message&&await this.loadCalendarAndSchedule(),!1;this.bookingKey=s.key??null,this.bookingNumber=s.number??null,this.isSubmitting=!0,this.serverError=null;try{await this.$nextTick();const a=await this.resolveCaptchaToken();await this.persistStatamicSubmission(a)}catch(a){console.warn("[BookingForm] Failed to persist Statamic submission",a)}finally{this.isSubmitting=!1,this.isWaitingForCaptcha=!1,this.isVerifyingOtp=!1}return this.isSuccess=!0,this.onSuccess(),!0},async resolveCaptchaToken(){if(this.captchaToken)return this.captchaToken;const e=this.getCaptchaWidget(),i=e?.dataset?.sitekey;if(!e||!i||typeof turnstile>"u")return null;try{return await new Promise(t=>{let s=!1;const a=(o=null)=>{s||(s=!0,clearTimeout(r),o&&(this.captchaToken=o),this.isWaitingForCaptcha=!1,t(this.captchaToken||null))},r=setTimeout(()=>a(this.captchaToken),4e3);if(this.turnstileWidgetId||(this.turnstileWidgetId=turnstile.render(e,{sitekey:i,execution:"execute",callback:o=>a(o),"error-callback":()=>a(null)})),!this.turnstileWidgetId){a(null);return}this.isWaitingForCaptcha=!0,turnstile.execute(this.turnstileWidgetId);const l=setInterval(()=>{(this.captchaToken||s)&&(clearInterval(l),this.captchaToken&&a(this.captchaToken))},100)})}catch(t){return console.warn("[BookingForm] Turnstile execute failed",t),this.isWaitingForCaptcha=!1,this.captchaToken||null}},async persistStatamicSubmission(e=null){const i=this.getNativeForm();if(!i){console.warn("[BookingForm] Statamic form element not found");return}const t=new FormData(i),s=e||this.captchaToken;s&&t.append("cf-turnstile-response",s);const a=await fetch(i.action,{method:i.method||"POST",headers:{"X-Requested-With":"XMLHttpRequest",Accept:"application/json"},body:t});let r=null;try{r=await a.json()}catch{r=null}a.ok&&(r?.success||r?.created||r?.submission_created)||console.warn("[BookingForm] Statamic persist did n
1ot succeed",a.status,r)},async submit(e){return this.submitAction(e,async()=>{let i=!0;const t=this.currentStep;for(let s=0;s<this.totalSteps;s++)this.currentStep=s,this.validateCurrentStep()||(i=!1);if(i){if(this.currentStep=t,this.$refs.captchaWidget&&!this.captchaToken&&typeof turnstile<"u")return this.isWaitingForCaptcha||(this.isWaitingForCaptcha=!0,this.isSubmitting=!0,this.turnstileWidgetId||this.initTurnstile(),this.turnstileWidgetId&&turnstile.execute(this.turnstileWidgetId)),!1}else for(let s=0;s<this.totalSteps;s++)if(this.errors[s]&&Object.keys(this.errors[s]).length>0){this.currentStep=s,this.updateVisibility();break}return i})},onSuccess(){this.availableDates=[],this.availableTimes=[],this.scheduleEntries=[],this.datesLoadedForStep=null,this.otpSessionKey=null,this.otpExpiresAt=null,this.otpError=null,this.otpResendInterval&&(clearInterval(this.otpResendInterval),this.otpResendInterval=null),this.otpResendSeconds=0}});export{_ as default};
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.