feat(web): show legal documents in registration dialogs

This commit is contained in:
richjohnrambo
2026-10-17 01:30:03 +08:00
parent dca34b8b5d
commit 8bdb8ed5a5
+34 -2
View File
@@ -1,4 +1,4 @@
import { useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import { Link, Navigate, useNavigate } from 'react-router-dom'; import { Link, Navigate, useNavigate } from 'react-router-dom';
@@ -33,9 +33,18 @@ export function RegisterPage() {
const [agreed, setAgreed] = useState(false); const [agreed, setAgreed] = useState(false);
const [submitting, setSubmitting] = useState(false); const [submitting, setSubmitting] = useState(false);
const [error, setError] = useState(''); const [error, setError] = useState('');
const [legalDocument, setLegalDocument] = useState<'terms' | 'privacy' | null>(null);
const legalDialogRef = useRef<HTMLDialogElement>(null);
const verifiedRef = useRef<{ key: string; response: string } | null>(null); const verifiedRef = useRef<{ key: string; response: string } | null>(null);
const submitRef = useRef(false); const submitRef = useRef(false);
useEffect(() => {
const dialog = legalDialogRef.current;
if (!dialog) return;
if (legalDocument && !dialog.open) dialog.showModal();
if (!legalDocument && dialog.open) dialog.close();
}, [legalDocument]);
if (!tenant) return <Navigate to="/tenant" replace />; if (!tenant) return <Navigate to="/tenant" replace />;
const minPasswordLength = tenant.passwordPolicy?.minLength ?? 8; const minPasswordLength = tenant.passwordPolicy?.minLength ?? 8;
// The deployed file endpoint is pinned to the IM API host; the web host serves // The deployed file endpoint is pinned to the IM API host; the web host serves
@@ -146,7 +155,7 @@ export function RegisterPage() {
autoComplete="new-password" maxLength={tenant.passwordPolicy?.maxLength} /> autoComplete="new-password" maxLength={tenant.passwordPolicy?.maxLength} />
<label className="flex items-start gap-2 text-sm text-text-secondary"> <label className="flex items-start gap-2 text-sm text-text-secondary">
<input type="checkbox" checked={agreed} onChange={(e) => setAgreed(e.target.checked)} className="mt-1" /> <input type="checkbox" checked={agreed} onChange={(e) => setAgreed(e.target.checked)} className="mt-1" />
<span>我已阅读并同意 <a href={`${legalOrigin}/legal/user-terms`} target="_blank" rel="noreferrer" className="text-accent">用户条款</a> 和 <a href={`${legalOrigin}/legal/privacy-policy`} target="_blank" rel="noreferrer" className="text-accent">隐私政策</a></span> <span>我已阅读并同意 <button type="button" onClick={() => setLegalDocument('terms')} className="cursor-pointer p-0 text-accent">用户条款</button> 和 <button type="button" onClick={() => setLegalDocument('privacy')} className="cursor-pointer p-0 text-accent">隐私政策</button></span>
</label> </label>
<button type="submit" disabled={submitting || !agreed || (!username && !phone)} <button type="submit" disabled={submitting || !agreed || (!username && !phone)}
className="h-[50px] rounded-md bg-btn text-base font-medium text-white disabled:bg-btn-disabled"> className="h-[50px] rounded-md bg-btn text-base font-medium text-white disabled:bg-btn-disabled">
@@ -154,6 +163,29 @@ export function RegisterPage() {
</button> </button>
</form> </form>
</main> </main>
<dialog
ref={legalDialogRef}
onClose={() => setLegalDocument(null)}
className="m-auto h-[min(88dvh,800px)] w-[min(92vw,800px)] overflow-hidden rounded-md border border-border-panel bg-white p-0 shadow-xl backdrop:bg-black/50"
aria-label={legalDocument === 'privacy' ? '隐私政策' : '用户条款'}
>
<div className="flex h-full min-h-0 flex-col">
<div className="flex h-14 shrink-0 items-center justify-between border-b border-border-panel px-5">
<h2 className="m-0 text-base font-semibold text-text-title">{legalDocument === 'privacy' ? '隐私政策' : '用户条款'}</h2>
<button type="button" onClick={() => legalDialogRef.current?.close()} aria-label="关闭协议弹窗" title="关闭"
className="grid h-9 w-9 place-items-center text-text-secondary hover:text-text-title">
<Icon name="close" className="text-[20px]" />
</button>
</div>
{legalDocument && <iframe
key={legalDocument}
title={legalDocument === 'privacy' ? '隐私政策正文' : '用户条款正文'}
src={`${legalOrigin}/legal/${legalDocument === 'privacy' ? 'privacy-policy' : 'user-terms'}`}
referrerPolicy="no-referrer"
className="min-h-0 w-full flex-1 border-0 bg-white"
/>}
</div>
</dialog>
</div> </div>
); );
} }