manual save(2026-01-22 17:13)
This commit is contained in:
@@ -8,6 +8,7 @@ import { Contact } from './pages/Contact'
|
||||
import { Cases } from './pages/Cases'
|
||||
import { Learning } from './pages/Learning'
|
||||
import { Assistant } from './pages/Assistant'
|
||||
import { Privacy } from './pages/Privacy'
|
||||
|
||||
// 页面切换动画配置
|
||||
const pageVariants = {
|
||||
@@ -74,6 +75,7 @@ function App() {
|
||||
<Route path="/news" element={<PageWrapper><News /></PageWrapper>} />
|
||||
<Route path="/learning" element={<PageWrapper><Learning /></PageWrapper>} />
|
||||
<Route path="/assistant" element={<PageWrapper><Assistant /></PageWrapper>} />
|
||||
<Route path="/privacy" element={<PageWrapper><Privacy /></PageWrapper>} />
|
||||
<Route path="/contact" element={<PageWrapper><Contact /></PageWrapper>} />
|
||||
|
||||
{/* English routes */}
|
||||
@@ -84,6 +86,7 @@ function App() {
|
||||
<Route path="/en/news" element={<PageWrapper><News /></PageWrapper>} />
|
||||
<Route path="/en/learning" element={<PageWrapper><Learning /></PageWrapper>} />
|
||||
<Route path="/en/assistant" element={<PageWrapper><Assistant /></PageWrapper>} />
|
||||
<Route path="/en/privacy" element={<PageWrapper><Privacy /></PageWrapper>} />
|
||||
<Route path="/en/contact" element={<PageWrapper><Contact /></PageWrapper>} />
|
||||
|
||||
<Route path="*" element={<NotFound />} />
|
||||
|
||||
127
src/components/PrivacyConsentModal.tsx
Normal file
127
src/components/PrivacyConsentModal.tsx
Normal file
@@ -0,0 +1,127 @@
|
||||
import React, { useId, useMemo, useState } from 'react'
|
||||
import { X } from 'lucide-react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { getLocaleFromPathname, withLocalePath } from '../lib/i18n'
|
||||
|
||||
type PrivacyConsentModalProps = {
|
||||
open: boolean
|
||||
onClose: () => void
|
||||
onConfirm: (payload: { consentedAt: string; version: string }) => void
|
||||
locale: 'zh' | 'en'
|
||||
}
|
||||
|
||||
const CONSENT_VERSION = '2026-01-01'
|
||||
|
||||
export const PrivacyConsentModal: React.FC<PrivacyConsentModalProps> = ({ open, onClose, onConfirm, locale }) => {
|
||||
const checkboxId = useId()
|
||||
const [agreed, setAgreed] = useState(false)
|
||||
|
||||
const privacyPath = useMemo(() => withLocalePath(locale, '/privacy'), [locale])
|
||||
|
||||
if (!open) return null
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-[100]">
|
||||
<div className="absolute inset-0 bg-black/40" aria-hidden="true" onClick={onClose} />
|
||||
|
||||
<div className="absolute inset-0 flex items-center justify-center p-4">
|
||||
<div
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-label={locale === 'en' ? 'Privacy consent' : '个人信息处理告知与同意'}
|
||||
className="w-full max-w-lg rounded-2xl bg-white shadow-xl border border-gray-100 overflow-hidden"
|
||||
>
|
||||
<div className="flex items-center justify-between px-6 py-4 border-b border-gray-100">
|
||||
<div>
|
||||
<div className="text-base font-semibold text-primary-dark">
|
||||
{locale === 'en' ? 'Privacy notice & consent' : '个人信息处理告知与同意'}
|
||||
</div>
|
||||
<div className="text-xs text-gray-500 mt-1">
|
||||
{locale === 'en' ? `Version: ${CONSENT_VERSION}` : `版本:${CONSENT_VERSION}`}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="w-9 h-9 inline-flex items-center justify-center rounded-lg text-gray-600 hover:bg-gray-50"
|
||||
aria-label={locale === 'en' ? 'Close' : '关闭'}
|
||||
>
|
||||
<X size={18} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="px-6 py-5 space-y-4 text-sm text-gray-700 leading-relaxed">
|
||||
<p>
|
||||
{locale === 'en'
|
||||
? 'To arrange a callback, we need to process your phone number and related inquiry information.'
|
||||
: '为安排专员回电沟通,我们需要处理你的手机号及与本次咨询相关的信息。'}
|
||||
</p>
|
||||
|
||||
<div className="rounded-xl bg-background border border-gray-100 p-4 space-y-2">
|
||||
<div className="font-medium text-primary-dark">{locale === 'en' ? 'What we collect' : '收集信息范围'}</div>
|
||||
<ul className="list-disc pl-5 space-y-1 text-gray-700">
|
||||
<li>{locale === 'en' ? 'Phone number (required)' : '手机号(必填)'}</li>
|
||||
<li>{locale === 'en' ? 'Inquiry description (required)' : '咨询需求描述(必填)'}</li>
|
||||
<li>{locale === 'en' ? 'Name/company (optional)' : '姓名/公司(选填)'}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="rounded-xl bg-background border border-gray-100 p-4 space-y-2">
|
||||
<div className="font-medium text-primary-dark">{locale === 'en' ? 'Purpose & retention' : '使用目的与保存期限'}</div>
|
||||
<p className="text-gray-700">
|
||||
{locale === 'en'
|
||||
? 'Used only for contact and follow-up regarding this inquiry. Retention period follows necessary business processing requirements and applicable laws.'
|
||||
: '仅用于与本次咨询相关的联系、答复与后续服务;保存期限以完成处理所必需的期限及法律法规要求为准。'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p className="text-xs text-gray-500">
|
||||
{locale === 'en' ? 'For details, see ' : '更多信息请查看'}
|
||||
<Link to={privacyPath} className="text-primary hover:text-primary-light underline underline-offset-2">
|
||||
{locale === 'en' ? 'Privacy Policy' : '《隐私政策》'}
|
||||
</Link>
|
||||
{locale === 'en' ? '.' : '。'}
|
||||
</p>
|
||||
|
||||
<label className="flex items-start gap-3 text-sm">
|
||||
<input
|
||||
id={checkboxId}
|
||||
type="checkbox"
|
||||
checked={agreed}
|
||||
onChange={(e) => setAgreed(e.target.checked)}
|
||||
className="mt-1 h-4 w-4 rounded border-gray-300 text-primary focus:ring-primary"
|
||||
/>
|
||||
<span>
|
||||
{locale === 'en'
|
||||
? 'I have read and agree to the privacy notice above and the Privacy Policy.'
|
||||
: '我已阅读并同意以上告知内容及《隐私政策》。'}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="px-6 py-4 border-t border-gray-100 flex items-center justify-end gap-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="px-4 py-2 rounded-xl text-sm font-medium text-gray-700 hover:bg-gray-50"
|
||||
>
|
||||
{locale === 'en' ? 'Cancel' : '取消'}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onConfirm({ consentedAt: new Date().toISOString(), version: CONSENT_VERSION })}
|
||||
disabled={!agreed}
|
||||
className={`px-4 py-2 rounded-xl text-sm font-medium text-white ${
|
||||
agreed ? 'bg-primary hover:bg-primary-light' : 'bg-gray-300 cursor-not-allowed'
|
||||
}`}
|
||||
>
|
||||
{locale === 'en' ? 'Agree & continue' : '同意并继续'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default PrivacyConsentModal
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
import { Header } from '../components/Header'
|
||||
import { Footer } from '../components/Footer'
|
||||
import { Breadcrumbs } from '../components/Breadcrumbs'
|
||||
import { PrivacyConsentModal } from '../components/PrivacyConsentModal'
|
||||
import { FormSubmissions, Forms, SearchResults } from '../clientsdk/sdk.gen'
|
||||
import type { Post, SearchResult, SearchResultQueryOperations } from '../clientsdk/types.gen'
|
||||
import { createClient } from '../clientsdk/client'
|
||||
@@ -182,8 +183,26 @@ export const Assistant: React.FC = () => {
|
||||
const [leadSubmitted, setLeadSubmitted] = useState(false)
|
||||
const [leadSubmitError, setLeadSubmitError] = useState<string | null>(null)
|
||||
|
||||
const [privacyOpen, setPrivacyOpen] = useState(false)
|
||||
const [pendingSubmitEvent, setPendingSubmitEvent] = useState<React.FormEvent | null>(null)
|
||||
const [privacyConsent, setPrivacyConsent] = useState<{ consentedAt: string; version: string } | null>(null)
|
||||
|
||||
const listRef = useRef<HTMLDivElement | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
const cached = localStorage.getItem('privacyConsent.callback')
|
||||
if (cached) {
|
||||
const parsed = JSON.parse(cached) as { consentedAt?: string; version?: string }
|
||||
if (parsed.consentedAt && parsed.version) {
|
||||
setPrivacyConsent({ consentedAt: parsed.consentedAt, version: parsed.version })
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const load = async () => {
|
||||
try {
|
||||
@@ -293,21 +312,25 @@ export const Assistant: React.FC = () => {
|
||||
return Object.keys(next).length === 0
|
||||
}
|
||||
|
||||
const submitLead = async (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
const doSubmitLead = async () => {
|
||||
setLeadSubmitError(null)
|
||||
|
||||
if (!validateLead()) return
|
||||
if (!privacyConsent) {
|
||||
setLeadSubmitError(locale === 'en' ? 'Please review and agree to the privacy notice first.' : '请先阅读并同意个人信息处理告知。')
|
||||
return
|
||||
}
|
||||
|
||||
setLeadSubmitting(true)
|
||||
|
||||
try {
|
||||
if (!leadFormId) {
|
||||
const cacheKey = 'chengyu_leads_local'
|
||||
const cacheKey = 'chengyu_callback_requests_local'
|
||||
const record = {
|
||||
...lead,
|
||||
createdAt: new Date().toISOString(),
|
||||
source: 'assistant',
|
||||
privacyConsentedAt: privacyConsent.consentedAt,
|
||||
privacyVersion: privacyConsent.version,
|
||||
}
|
||||
|
||||
const existing = JSON.parse(localStorage.getItem(cacheKey) || '[]')
|
||||
@@ -329,6 +352,8 @@ export const Assistant: React.FC = () => {
|
||||
{ field: 'company', value: lead.company.trim() },
|
||||
{ field: 'demand', value: lead.demand.trim() },
|
||||
{ field: 'source', value: 'AI智能助手' },
|
||||
{ field: 'privacyConsentedAt', value: privacyConsent.consentedAt },
|
||||
{ field: 'privacyVersion', value: privacyConsent.version },
|
||||
].filter((item) => item.value),
|
||||
},
|
||||
})
|
||||
@@ -343,6 +368,16 @@ export const Assistant: React.FC = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const submitLead = async (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
setLeadSubmitError(null)
|
||||
|
||||
if (!validateLead()) return
|
||||
|
||||
setPendingSubmitEvent(e)
|
||||
setPrivacyOpen(true)
|
||||
}
|
||||
|
||||
return (
|
||||
<motion.div className="min-h-screen bg-background" initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ duration: 0.4 }}>
|
||||
<Header />
|
||||
@@ -513,8 +548,8 @@ export const Assistant: React.FC = () => {
|
||||
</h2>
|
||||
<p className="mt-1 text-sm text-gray-600 leading-relaxed">
|
||||
{locale === 'en'
|
||||
? 'Phone number is required. The ops team can process submissions in the backend.'
|
||||
: '手机号为必填项;提交后,后台运营人员可及时查看并处理(可在运营平台表单里配置邮件通知)。'}
|
||||
? 'Phone number is required for a callback. We will ask for your consent before submission.'
|
||||
: '手机号为预约回电所必需;提交前会弹出“个人信息处理告知”,经你同意后才会提交。'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -543,7 +578,7 @@ export const Assistant: React.FC = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<form onSubmit={submitLead} className="mt-6 space-y-5">
|
||||
<form onSubmit={submitLead} className="mt-6 space-y-5" aria-label={locale === 'en' ? 'Callback request form' : '预约回电表单'}>
|
||||
<div className="grid sm:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label htmlFor="leadName" className="block text-sm font-medium text-gray-700 mb-2">
|
||||
@@ -644,21 +679,41 @@ export const Assistant: React.FC = () => {
|
||||
</button>
|
||||
|
||||
<div className="text-xs text-gray-500 leading-relaxed">
|
||||
{leadFormLoading ? (
|
||||
<span>{locale === 'en' ? 'Loading backend form configuration…' : '正在加载后台表单配置…'}</span>
|
||||
) : leadFormId ? (
|
||||
<span>
|
||||
{locale === 'en'
|
||||
? 'Your request has been submitted successfully.'
|
||||
: '你的预约请求已提交,我们会尽快与你联系。'}
|
||||
<div className="flex flex-wrap items-center gap-x-3 gap-y-1">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setPrivacyOpen(true)}
|
||||
className="text-primary hover:text-primary-light underline underline-offset-2"
|
||||
>
|
||||
{locale === 'en' ? 'Privacy notice' : '个人信息处理告知'}
|
||||
</button>
|
||||
<span className="text-gray-300" aria-hidden="true">
|
||||
|
|
||||
</span>
|
||||
) : (
|
||||
<span>
|
||||
{locale === 'en'
|
||||
? 'Backend form not found. Please create a form titled "AI Assistant Callback".'
|
||||
: '当前未配置预约表单。请在运营平台创建“AI智能助手-预约回电”表单,并按需开启邮件/Webhook 通知,确保咨询请求能及时处理。'}
|
||||
</span>
|
||||
)}
|
||||
<a
|
||||
href={locale === 'en' ? '/en/privacy' : '/privacy'}
|
||||
className="text-primary hover:text-primary-light underline underline-offset-2"
|
||||
>
|
||||
{locale === 'en' ? 'Privacy policy' : '隐私政策'}
|
||||
</a>
|
||||
</div>
|
||||
<div className="mt-2">
|
||||
{leadFormLoading ? (
|
||||
<span>{locale === 'en' ? 'Loading backend form configuration…' : '正在加载后台表单配置…'}</span>
|
||||
) : leadFormId ? (
|
||||
<span>
|
||||
{locale === 'en'
|
||||
? 'Your request has been submitted successfully.'
|
||||
: '你的预约请求已提交,我们会尽快与你联系。'}
|
||||
</span>
|
||||
) : (
|
||||
<span>
|
||||
{locale === 'en'
|
||||
? 'Backend form not found. Please create a form titled "AI Assistant Callback".'
|
||||
: '当前未配置预约表单。请在运营平台创建“AI智能助手-预约回电”表单,并按需开启邮件/Webhook 通知,确保咨询请求能及时处理。'}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -716,6 +771,27 @@ export const Assistant: React.FC = () => {
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<PrivacyConsentModal
|
||||
open={privacyOpen}
|
||||
locale={locale}
|
||||
onClose={() => {
|
||||
setPrivacyOpen(false)
|
||||
setPendingSubmitEvent(null)
|
||||
}}
|
||||
onConfirm={(payload) => {
|
||||
try {
|
||||
localStorage.setItem('privacyConsent.callback', JSON.stringify(payload))
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
|
||||
setPrivacyConsent(payload)
|
||||
setPrivacyOpen(false)
|
||||
setPendingSubmitEvent(null)
|
||||
void doSubmitLead()
|
||||
}}
|
||||
/>
|
||||
|
||||
<Footer />
|
||||
</motion.div>
|
||||
)
|
||||
|
||||
148
src/pages/Privacy.tsx
Normal file
148
src/pages/Privacy.tsx
Normal file
@@ -0,0 +1,148 @@
|
||||
import React, { useMemo } from 'react'
|
||||
import { motion } from 'framer-motion'
|
||||
import { Header } from '../components/Header'
|
||||
import { Footer } from '../components/Footer'
|
||||
import { Breadcrumbs } from '../components/Breadcrumbs'
|
||||
import { usePageMeta } from '../hooks/usePageMeta'
|
||||
import { getLocaleFromPathname } from '../lib/i18n'
|
||||
|
||||
export const Privacy: React.FC = () => {
|
||||
const locale = useMemo(() => getLocaleFromPathname(window.location.pathname), [])
|
||||
|
||||
usePageMeta({
|
||||
title: locale === 'en' ? 'Privacy Policy' : '隐私政策',
|
||||
description:
|
||||
locale === 'en'
|
||||
? 'Privacy policy and personal information processing notice.'
|
||||
: '个人信息处理规则与隐私政策说明。',
|
||||
locale,
|
||||
})
|
||||
|
||||
return (
|
||||
<motion.div className="min-h-screen bg-background" initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ duration: 0.4 }}>
|
||||
<Header />
|
||||
|
||||
<main>
|
||||
<section className="pt-28 pb-14 bg-gradient-to-br from-primary to-primary-light">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="text-white">
|
||||
<Breadcrumbs items={[{ label: locale === 'en' ? 'Privacy Policy' : '隐私政策', href: '/privacy' }]} className="mb-5" />
|
||||
<h1 className="text-4xl md:text-5xl font-bold tracking-tight">{locale === 'en' ? 'Privacy Policy' : '隐私政策'}</h1>
|
||||
<p className="mt-4 text-lg text-white/85 max-w-3xl">
|
||||
{locale === 'en'
|
||||
? 'This policy describes how we process personal information in connection with website inquiries and callback requests.'
|
||||
: '本政策用于说明我们在网站咨询与“预约回电”等场景下对个人信息的处理规则。'}
|
||||
</p>
|
||||
<p className="mt-2 text-xs text-white/70">{locale === 'en' ? 'Effective date: 2026-01-01' : '生效日期:2026-01-01'}</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="py-14 -mt-8">
|
||||
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="bg-white rounded-2xl shadow-sm border border-gray-100 p-8 space-y-8 text-sm leading-relaxed text-gray-700">
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-primary-dark">{locale === 'en' ? '1. Scope' : '一、适用范围'}</h2>
|
||||
<p className="mt-3">
|
||||
{locale === 'en'
|
||||
? 'This policy applies to personal information processing when you use our website features such as inquiries, AI assistant, and callback requests.'
|
||||
: '本政策适用于你使用本网站提供的咨询、AI智能助手、预约回电等功能时,我们对个人信息的处理活动。'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-primary-dark">{locale === 'en' ? '2. What we collect' : '二、我们收集哪些信息'}</h2>
|
||||
<ul className="mt-3 list-disc pl-5 space-y-2">
|
||||
<li>{locale === 'en' ? 'Phone number (required for callback requests).' : '手机号(用于预约回电时必填)。'}</li>
|
||||
<li>{locale === 'en' ? 'Inquiry description and related information you provide.' : '你主动填写的咨询内容及相关信息。'}</li>
|
||||
<li>{locale === 'en' ? 'Optional name/company information.' : '姓名、公司等选填信息(如你提供)。'}</li>
|
||||
<li>{locale === 'en' ? 'Basic device/browser information for security and performance.' : '为保障安全与服务稳定性所需的基础设备/浏览器信息(通常由浏览器自动提供)。'}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-primary-dark">{locale === 'en' ? '3. Purpose and legal basis' : '三、使用目的与处理依据'}</h2>
|
||||
<ul className="mt-3 list-disc pl-5 space-y-2">
|
||||
<li>
|
||||
{locale === 'en'
|
||||
? 'To contact you and respond to your inquiry / arrange a callback.'
|
||||
: '用于联系你、答复你的咨询、安排专员回电沟通。'}
|
||||
</li>
|
||||
<li>
|
||||
{locale === 'en'
|
||||
? 'To ensure service security, prevent abuse, and improve performance.'
|
||||
: '用于保障服务安全、防范滥用与提升服务稳定性。'}
|
||||
</li>
|
||||
</ul>
|
||||
<p className="mt-3 text-gray-700">
|
||||
{locale === 'en'
|
||||
? 'For scenarios requiring consent (e.g., collecting your phone number for callback), we will obtain your explicit consent before submission.'
|
||||
: '对于需要取得同意的场景(例如:收集手机号用于回电沟通),我们会在你提交前通过弹框获得你的明确同意。'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-primary-dark">{locale === 'en' ? '4. Retention' : '四、保存期限'}</h2>
|
||||
<p className="mt-3">
|
||||
{locale === 'en'
|
||||
? 'We retain your personal information for the minimum period necessary to complete the processing of your inquiry and comply with applicable laws.'
|
||||
: '我们仅在实现本政策所述目的所必需的最短期限内保存个人信息,并在法律法规要求的范围内留存必要记录。'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-primary-dark">{locale === 'en' ? '5. Sharing and disclosure' : '五、共享、转让与公开披露'}</h2>
|
||||
<p className="mt-3">
|
||||
{locale === 'en'
|
||||
? 'We do not sell personal information. We may share information with authorized personnel and service providers only as needed for inquiry processing, and under appropriate confidentiality and security measures.'
|
||||
: '我们不会出售个人信息。仅在处理咨询所必需的范围内,与经授权的工作人员及必要的服务提供方共享,并采取相应的保密与安全措施。'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-primary-dark">{locale === 'en' ? '6. Security measures' : '六、安全保护措施'}</h2>
|
||||
<ul className="mt-3 list-disc pl-5 space-y-2">
|
||||
<li>{locale === 'en' ? 'Access control and least-privilege principle.' : '权限控制与最小必要原则。'}</li>
|
||||
<li>{locale === 'en' ? 'Audit and monitoring for abnormal access.' : '对异常访问进行审计与监测。'}</li>
|
||||
<li>{locale === 'en' ? 'Secure storage and transmission where applicable.' : '在适用情况下采取安全存储与传输措施。'}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-primary-dark">{locale === 'en' ? '7. Your rights' : '七、你的权利'}</h2>
|
||||
<ul className="mt-3 list-disc pl-5 space-y-2">
|
||||
<li>{locale === 'en' ? 'Access, correction, and deletion as permitted by law.' : '在法律允许范围内的查阅、更正、删除等权利。'}</li>
|
||||
<li>{locale === 'en' ? 'Withdraw consent (will not affect processing already performed).' : '撤回同意(不影响撤回前已进行的处理活动的效力)。'}</li>
|
||||
</ul>
|
||||
<p className="mt-3 text-gray-700">
|
||||
{locale === 'en'
|
||||
? 'You can stop providing information at any time by not submitting the form.'
|
||||
: '你可以随时选择不再提交信息(例如:不提交“预约回电”表单),以停止进一步提供个人信息。'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-primary-dark">{locale === 'en' ? '8. Contact' : '八、联系我们'}</h2>
|
||||
<p className="mt-3">
|
||||
{locale === 'en'
|
||||
? 'If you have any questions about this policy, please contact us via the Contact page.'
|
||||
: '如你对本政策有任何疑问,可通过“联系我们”页面与我们取得联系。'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p className="text-xs text-gray-500">
|
||||
{locale === 'en'
|
||||
? 'Note: This is a template policy. Please have legal/compliance review and update fields such as entity name, contact, retention and security details.'
|
||||
: '提示:本页面为模板化隐私政策内容。正式上线前建议由法务/合规审核,并补齐主体名称、联系方式、保存期限与安全措施等细节。'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
</motion.div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Privacy
|
||||
Reference in New Issue
Block a user