feat: добавлена функциональность политики конфиденциальности и согласия на обработку ПД
Основные изменения: Backend: - Добавлена миграция для поля privacy_consented_at в таблицу telecart_customers - Создан PrivacyPolicyHandler с методами: * checkIsUserPrivacyConsented - проверка наличия согласия пользователя * userPrivacyConsent - сохранение согласия пользователя - Обновлен TelegramService для извлечения userId из initData - Обновлен TelegramServiceProvider для внедрения зависимостей - Добавлены новые маршруты в routes.php - Обновлен SettingsHandler для возврата privacy_policy_link - Обновлен TelegramCustomersHandler для включения privacy_consented_at в ответы - Обновлены тесты TelegramServiceTest Frontend (SPA): - Создан компонент PrivacyPolicy.vue для отображения запроса согласия - Добавлена проверка согласия при инициализации приложения (main.js) - Обновлен App.vue для отображения компонента PrivacyPolicy - Добавлены функции checkIsUserPrivacyConsented и userPrivacyConsent в ftch.js - Обновлен SettingsStore для хранения privacy_policy_link и is_privacy_consented Frontend (Admin): - Добавлено поле privacy_policy_link в настройки (settings.js) - Добавлена настройка ссылки на политику конфиденциальности в GeneralView.vue - Обновлен CustomersView.vue: * Добавлена колонка privacy_consented_at с отображением даты согласия * Добавлена поддержка help-текста для колонок с иконкой вопроса и tooltip * Добавлены help-тексты для колонок last_seen_at, privacy_consented_at, created_at * Улучшено форматирование кода
This commit is contained in:
@@ -17,6 +17,8 @@
|
||||
</KeepAlive>
|
||||
</RouterView>
|
||||
|
||||
<PrivacyPolicy v-if="! settings.is_privacy_consented"/>
|
||||
|
||||
<CartButton v-if="settings.store_enabled"/>
|
||||
<Dock v-if="isAppDockShown"/>
|
||||
</section>
|
||||
@@ -42,8 +44,8 @@ import {useSettingsStore} from "@/stores/SettingsStore.js";
|
||||
import {useProductFiltersStore} from "@/stores/ProductFiltersStore.js";
|
||||
import CartButton from "@/components/CartButton.vue";
|
||||
import Dock from "@/components/Dock.vue";
|
||||
import Navbar from "@/components/Navbar.vue";
|
||||
import AppDebugMessage from "@/components/AppDebugMessage.vue";
|
||||
import PrivacyPolicy from "@/components/PrivacyPolicy.vue";
|
||||
|
||||
const tg = useMiniApp();
|
||||
const platform = ref();
|
||||
|
||||
42
frontend/spa/src/components/PrivacyPolicy.vue
Normal file
42
frontend/spa/src/components/PrivacyPolicy.vue
Normal file
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<div v-if="isShown" class="toast toast-center bottom-20 z-50">
|
||||
<div class="alert alert-info">
|
||||
<span>
|
||||
Используя магазин, вы соглашаетесь с
|
||||
<a v-if="settings.privacy_policy_link"
|
||||
href="#" class="underline"
|
||||
@click.prevent="showPrivacyPolicy"
|
||||
>обработкой персональных данных</a>
|
||||
<span v-else>обработкой персональных данных</span>.
|
||||
</span>
|
||||
<button
|
||||
class="btn btn-outline"
|
||||
@click="privacyConsent"
|
||||
>
|
||||
OK
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {userPrivacyConsent} from "@/utils/ftch.js";
|
||||
import {ref} from "vue";
|
||||
import {useSettingsStore} from "@/stores/SettingsStore.js";
|
||||
|
||||
const isShown = ref(true);
|
||||
const settings = useSettingsStore();
|
||||
|
||||
async function privacyConsent() {
|
||||
isShown.value = false;
|
||||
await userPrivacyConsent();
|
||||
}
|
||||
|
||||
function showPrivacyPolicy() {
|
||||
if (settings.privacy_policy_link) {
|
||||
window.Telegram.WebApp.openLink(settings.privacy_policy_link, {
|
||||
try_instant_view: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -8,7 +8,7 @@ import {useSettingsStore} from "@/stores/SettingsStore.js";
|
||||
import ApplicationError from "@/ApplicationError.vue";
|
||||
import AppMetaInitializer from "@/utils/AppMetaInitializer.ts";
|
||||
import {injectYaMetrika} from "@/utils/yaMetrika.js";
|
||||
import {saveTelegramCustomer} from "@/utils/ftch.js";
|
||||
import {checkIsUserPrivacyConsented, saveTelegramCustomer} from "@/utils/ftch.js";
|
||||
|
||||
import {register} from 'swiper/element/bundle';
|
||||
import 'swiper/element/bundle';
|
||||
@@ -59,6 +59,22 @@ settings.load()
|
||||
}
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
(async () => {
|
||||
try {
|
||||
const response = await checkIsUserPrivacyConsented();
|
||||
settings.is_privacy_consented = response?.data?.is_privacy_consented;
|
||||
if (settings.is_privacy_consented) {
|
||||
console.info('[Init] Privacy Policy consent given by user.');
|
||||
} else {
|
||||
console.info('[Init] Privacy Policy consent NOT given by user.');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Init] Failed to check Telegram user consent.');
|
||||
settings.is_privacy_consented = false;
|
||||
}
|
||||
})();
|
||||
})
|
||||
.then(() => blocks.processBlocks(settings.mainpage_blocks))
|
||||
.then(async () => {
|
||||
console.debug('Load default filters for the main page');
|
||||
|
||||
@@ -29,6 +29,8 @@ export const useSettingsStore = defineStore('settings', {
|
||||
text_order_created_success: 'Заказ успешно оформлен.',
|
||||
},
|
||||
mainpage_blocks: [],
|
||||
is_privacy_consented: false,
|
||||
privacy_policy_link: false,
|
||||
}),
|
||||
|
||||
actions: {
|
||||
@@ -53,6 +55,7 @@ export const useSettingsStore = defineStore('settings', {
|
||||
this.currency_code = settings.currency_code;
|
||||
this.texts = settings.texts;
|
||||
this.mainpage_blocks = settings.mainpage_blocks;
|
||||
this.privacy_policy_link = settings.privacy_policy_link;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -107,4 +107,12 @@ export async function saveTelegramCustomer(userData) {
|
||||
});
|
||||
}
|
||||
|
||||
export async function checkIsUserPrivacyConsented() {
|
||||
return await ftch('checkIsUserPrivacyConsented');
|
||||
}
|
||||
|
||||
export async function userPrivacyConsent() {
|
||||
return await ftch('userPrivacyConsent');
|
||||
}
|
||||
|
||||
export default ftch;
|
||||
|
||||
Reference in New Issue
Block a user