Skip to content

Commit 78ebfc0

Browse files
feat: Add chat locking and policy pop-ups
1 parent 1f071ea commit 78ebfc0

File tree

4 files changed

+495
-6
lines changed

4 files changed

+495
-6
lines changed

frontend/src/App.jsx

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,20 @@ import Design from './pages/Design';
99
import SplashScreen from './components/SplashScreen';
1010
import { applyTheme, getStoredTheme } from './theme';
1111
import Explore from './pages/Explore';
12+
import PolicyModal from './components/PolicyModal';
13+
import { privacyPolicyContent, termsOfServiceContent, cookiePolicyContent } from './data/policies';
1214

1315
export default function App() {
1416
// Detect GitHub Pages subdirectory
1517
const isGitHubPages = window.location.hostname.includes('github.io');
1618
const routerBase = isGitHubPages ? '/myAIchatbotProject' : '/';
1719
const [showSplash, setShowSplash] = useState(() => localStorage.getItem('showSplash') !== 'false');
1820
const [splashVisible, setSplashVisible] = useState(showSplash);
21+
22+
// Policy modal state
23+
const [policyModalOpen, setPolicyModalOpen] = useState(false);
24+
const [policyTitle, setPolicyTitle] = useState('');
25+
const [policyContent, setPolicyContent] = useState(null);
1926

2027
useEffect(() => {
2128
const { theme, mode } = getStoredTheme();
@@ -28,6 +35,33 @@ export default function App() {
2835
return () => clearTimeout(timer);
2936
}, [showSplash]);
3037

38+
// Functions to open policy modals
39+
const openPrivacyPolicy = (e) => {
40+
e.preventDefault();
41+
setPolicyTitle('Chính sách Quyền riêng tư');
42+
setPolicyContent(privacyPolicyContent);
43+
setPolicyModalOpen(true);
44+
};
45+
46+
const openTermsOfService = (e) => {
47+
e.preventDefault();
48+
setPolicyTitle('Điều khoản Dịch vụ');
49+
setPolicyContent(termsOfServiceContent);
50+
setPolicyModalOpen(true);
51+
};
52+
53+
const openCookiePolicy = (e) => {
54+
e.preventDefault();
55+
setPolicyTitle('Chính sách Cookie');
56+
setPolicyContent(cookiePolicyContent);
57+
setPolicyModalOpen(true);
58+
};
59+
60+
const closePolicyModal = () => {
61+
setPolicyModalOpen(false);
62+
setPolicyContent(null);
63+
};
64+
3165
return (
3266
<BrowserRouter basename={routerBase}>
3367
<div className="min-h-screen app-shell text-[var(--c-text)]">
@@ -69,7 +103,7 @@ export default function App() {
69103
<div className="space-y-3">
70104
<div className="text-sm font-semibold text-[var(--c-text)]">Resources</div>
71105
<div className="flex flex-col gap-2 text-sm">
72-
<a className="text-[#5B5B57] hover:text-[var(--c-text)] hover:underline" href="/">Chat</a>
106+
<a className="text-[#5B5B57] hover:text-[var(--c-text)] hover:underline" href="https://thisisanvariableofacoder.github.io/myAIchatbotProject">Chat</a>
73107
<a className="text-[#5B5B57] hover:text-[var(--c-text)] hover:underline" href="/explore">Explore</a>
74108
<a
75109
className="text-[#5B5B57] hover:text-[var(--c-text)] hover:underline"
@@ -85,9 +119,9 @@ export default function App() {
85119
<div className="space-y-3">
86120
<div className="text-sm font-semibold text-[var(--c-text)]">Legal</div>
87121
<div className="flex flex-col gap-2 text-sm">
88-
<a className="text-[#5B5B57] hover:text-[var(--c-text)] hover:underline" href="#">Privacy policy</a>
89-
<a className="text-[#5B5B57] hover:text-[var(--c-text)] hover:underline" href="#">Terms of service</a>
90-
<a className="text-[#5B5B57] hover:text-[var(--c-text)] hover:underline" href="#">Cookie policy</a>
122+
<button onClick={openPrivacyPolicy} className="text-left text-[#5B5B57] hover:text-[var(--c-text)] hover:underline">Privacy policy</button>
123+
<button onClick={openTermsOfService} className="text-left text-[#5B5B57] hover:text-[var(--c-text)] hover:underline">Terms of service</button>
124+
<button onClick={openCookiePolicy} className="text-left text-[#5B5B57] hover:text-[var(--c-text)] hover:underline">Cookie policy</button>
91125
</div>
92126
</div>
93127
</div>
@@ -108,6 +142,12 @@ export default function App() {
108142
</div>
109143
</footer>
110144
{splashVisible && <SplashScreen onClose={() => setSplashVisible(false)} />}
145+
<PolicyModal
146+
isOpen={policyModalOpen}
147+
onClose={closePolicyModal}
148+
title={policyTitle}
149+
content={policyContent}
150+
/>
111151
</div>
112152
</BrowserRouter>
113153
);
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { useEffect } from 'react';
2+
3+
/**
4+
* PolicyModal Component
5+
* Displays privacy policy, terms of service, or cookie policy in a modal overlay
6+
*/
7+
export default function PolicyModal({ isOpen, onClose, title, content }) {
8+
// Prevent body scroll when modal is open
9+
useEffect(() => {
10+
if (isOpen) {
11+
document.body.style.overflow = 'hidden';
12+
} else {
13+
document.body.style.overflow = 'unset';
14+
}
15+
return () => {
16+
document.body.style.overflow = 'unset';
17+
};
18+
}, [isOpen]);
19+
20+
if (!isOpen) return null;
21+
22+
return (
23+
<div
24+
className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm"
25+
onClick={onClose}
26+
>
27+
<div
28+
className="relative bg-[var(--c-bg)] rounded-xl shadow-2xl max-w-3xl w-full max-h-[85vh] overflow-hidden flex flex-col animate-rise"
29+
onClick={(e) => e.stopPropagation()}
30+
>
31+
{/* Header */}
32+
<div className="flex items-center justify-between p-6 border-b border-[var(--c-border)]">
33+
<h2 className="text-xl font-bold text-[var(--c-text)]">{title}</h2>
34+
<button
35+
onClick={onClose}
36+
className="flex items-center justify-center w-8 h-8 rounded-full bg-[var(--c-surface)] hover:bg-[var(--c-hover)] text-[var(--c-text)] transition-colors"
37+
aria-label="Close"
38+
>
39+
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
40+
<line x1="18" y1="6" x2="6" y2="18"></line>
41+
<line x1="6" y1="6" x2="18" y2="18"></line>
42+
</svg>
43+
</button>
44+
</div>
45+
46+
{/* Content */}
47+
<div className="flex-1 overflow-y-auto p-6 prose prose-sm max-w-none text-[var(--c-text)]">
48+
{content}
49+
</div>
50+
51+
{/* Footer */}
52+
<div className="p-4 border-t border-[var(--c-border)] flex justify-end">
53+
<button
54+
onClick={onClose}
55+
className="px-6 py-2 bg-[var(--c-primary)] text-white rounded-lg hover:opacity-90 transition-opacity font-medium"
56+
>
57+
Đóng
58+
</button>
59+
</div>
60+
</div>
61+
</div>
62+
);
63+
}

0 commit comments

Comments
 (0)