diff --git a/app.vue b/app.vue index ce9b294..f1dff5c 100644 --- a/app.vue +++ b/app.vue @@ -1,8 +1,9 @@ + diff --git a/assets/main.scss b/assets/main.scss index bd8ac3e..c76956e 100644 --- a/assets/main.scss +++ b/assets/main.scss @@ -455,7 +455,7 @@ button { } .canvas-icons { - @apply absolute text-3xl top-0 left-0 flex flex-col; + @apply absolute text-3xl flex flex-col; a { @apply cursor-pointer; diff --git a/components/modal.vue b/components/modal.vue new file mode 100644 index 0000000..80d50fc --- /dev/null +++ b/components/modal.vue @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + {{ modalStatus[modal_state.show_status][1] }} + + + + Оставьте контакты для связи + + + + + + + + Нажимая кнопку "Отправить", вы даете согласие на + + обработку персональных данных. + + + + + + Отправить + + Отмена + + + + + + + Отправить расчет на e-mail + + + + + + diff --git a/components/model/scene.vue b/components/model/scene.vue index 63044df..d803b79 100644 --- a/components/model/scene.vue +++ b/components/model/scene.vue @@ -1,89 +1,88 @@ - - - - - Загрузка 3D модели - - - - - - - - - - - - - - - - - - + + + + Загрузка 3D модели + + + + + + + + + + + + + + + + + + + + Рассчитать - Рассчитать diff --git a/composables/modal.ts b/composables/modal.ts new file mode 100644 index 0000000..5ea3f1a --- /dev/null +++ b/composables/modal.ts @@ -0,0 +1,145 @@ +// useModalState.ts +import { ref, reactive, watch } from 'vue'; +import { useRuntimeConfig } from '#app'; + +type ModalDataType = { + phone?: string; + name?: string; + email?: string; + policy: boolean; +}; + +// Состояние открытия модального окна +const isModalOpen = ref(false); +// Реактивные данные формы +const modal_data = reactive({ + email: undefined, + phone: undefined, + name: undefined, + policy: false, +}); +// Реактивные данные состояния формы +const modal_form = reactive({ + disabled: true, + errors: [] as string[], +}); + +// Реактивные данные состояния модального окна +const modal_state = reactive({ + show_form: true, + show_status: null as null | 'loading' | 'success' | 'error', +}); +export function useModalState(initialState = false) { + const config = useRuntimeConfig(); + const apiBase = config.public.apiBase; + + // Функция для открытия модального окна + const openModal = () => { + isModalOpen.value = true; + }; + + // Функция для закрытия модального окна + const closeModal = () => { + resetModalData(); + isModalOpen.value = false; + }; + + // Функция для переключения состояния модального окна + const toggleModal = () => { + resetModalData(); + isModalOpen.value = !isModalOpen.value; + }; + + // Функция для сброса данных формы + const resetModalData = () => { + modal_data.email = undefined; + modal_data.phone = undefined; + modal_data.name = undefined; + modal_data.policy = false; + + modal_state.show_form = true; + modal_state.show_status = null; + + modal_form.disabled = true; + modal_form.errors = []; + }; + + // Функция для открытия формы + const openForm = () => { + modal_state.show_form = !modal_state.show_form; + goal('open_form'); + }; + + // Валидация ввода + const validateInput = (evt: KeyboardEvent) => { + const valid_symbols = /[a-zA-Z0-9\+(\\)\ @\.]/; + if (!valid_symbols.test(evt.key)) { + evt.preventDefault(); + } + }; + + // Валидация данных формы + const validate = () => { + const phone_regexp = /^\+?[\d\s-()]{0,14}\d{11}$/; + const email_regex = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/; + + modal_form.disabled = true; + if ( + ((modal_data.phone && phone_regexp.test(modal_data.phone)) || + (modal_data.email && email_regex.test(modal_data.email))) && + modal_data.policy === true + ) { + modal_form.disabled = false; + } + }; + + // Отправка данных на сервер + const submit = async () => { + goal('submit_form', modal_data); + modal_state.show_status = 'loading'; + try { + const res = await fetch(`${apiBase}/custom_request/`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + name: modal_data.name || `ref from site ${new Date()}`, + phone: modal_data.phone, + email: modal_data.email, + privacy: true, + }), + }); + modal_state.show_status = 'success'; + } catch (error) { + modal_state.show_status = 'error'; + } + }; + + // Наблюдение за изменениями данных формы + watch(modal_data, validate); + + // Наблюдение за состоянием модального окна + watch(isModalOpen, (newValue) => { + console.log(isModalOpen) + if (newValue) { + document.body.classList.add('modal-opened'); + } else { + document.body.classList.remove('modal-opened'); + } + }); + + return { + isModalOpen, + modal_data, + modal_form, + modal_state, + openModal, + closeModal, + toggleModal, + openForm, + validateInput, + validate, + submit, + }; +} \ No newline at end of file diff --git a/utils/goal.ts b/utils/goal.ts new file mode 100644 index 0000000..ccd5d82 --- /dev/null +++ b/utils/goal.ts @@ -0,0 +1,6 @@ +export default (target: string, params: object = {}) => { + const nuxtApp = useNuxtApp() + if (nuxtApp.$metrika) { + (nuxtApp.$metrika as any).reachGoal(target, params || {}) + } +} \ No newline at end of file