Legal, TM, ToS, Privacy Policy
All checks were successful
ci/woodpecker/manual/woodpecker Pipeline was successful

This commit is contained in:
Josh 2025-09-29 15:50:20 +00:00
parent bbde9f2da2
commit caa78298ec
6 changed files with 342 additions and 5 deletions

View File

@ -1 +1 @@
be65400be96a473622c09b3df6073c5837dacc82-372bcf506971f56c4911b429b9f5de5bc37ed008-e9eccd451b778829eb2f2c9752c670b707e1268b c5704005f291ecf264cdc92403119e7db5831e61-372bcf506971f56c4911b429b9f5de5bc37ed008-e9eccd451b778829eb2f2c9752c670b707e1268b

View File

@ -48,6 +48,8 @@ import * as safeLocal from './utils/safeLocal.js';
import VerificationGate from './components/VerificationGate.js'; import VerificationGate from './components/VerificationGate.js';
import Verify from './components/Verify.js'; import Verify from './components/Verify.js';
import { initNetObserver } from './utils/net.js'; import { initNetObserver } from './utils/net.js';
import PrivacyPolicy from './components/PrivacyPolicy.js';
import TermsOfService from './components/TermsOfService.js';
@ -225,7 +227,9 @@ if (loggingOut) return;
location.pathname.startsWith('/reset-password') || location.pathname.startsWith('/reset-password') ||
location.pathname === '/signin' || location.pathname === '/signin' ||
location.pathname === '/signup' || location.pathname === '/signup' ||
location.pathname === '/forgot-password' location.pathname === '/forgot-password' ||
location.pathname === '/privacy' ||
location.pathname === '/terms'
) { ) {
try { localStorage.removeItem('id'); } catch {} try { localStorage.removeItem('id'); } catch {}
setIsAuthenticated(false); setIsAuthenticated(false);
@ -260,7 +264,9 @@ if (loggingOut) return;
p === '/signup' || p === '/signup' ||
p === '/forgot-password' || p === '/forgot-password' ||
p.startsWith('/reset-password') || p.startsWith('/reset-password') ||
p === '/paywall'; p === '/paywall' ||
p === '/privacy' ||
p === '/terms';
if (!onPublic) navigate('/signin?session=expired', { replace: true }); if (!onPublic) navigate('/signin?session=expired', { replace: true });
} finally { } finally {
if (!cancelled) setIsLoading(false); if (!cancelled) setIsLoading(false);
@ -372,7 +378,7 @@ const cancelLogout = () => {
{/* Header */} {/* Header */}
<header className="sticky top-0 z-40 flex items-center justify-between border-b bg-white/95 px-4 py-3 md:px-6 md:py-4 backdrop-blur supports-[backdrop-filter]:bg-white/70"> <header className="sticky top-0 z-40 flex items-center justify-between border-b bg-white/95 px-4 py-3 md:px-6 md:py-4 backdrop-blur supports-[backdrop-filter]:bg-white/70">
<h1 className="text-base md:text-lg font-semibold truncate pr-3"> <h1 className="text-base md:text-lg font-semibold truncate pr-3">
AptivaAI - Career Guidance Platform AptivaAI<span className="align-super text-sm"></span> - Career Guidance Platform
</h1> </h1>
{/* Mobile hamburger */} {/* Mobile hamburger */}
@ -836,6 +842,8 @@ const cancelLogout = () => {
<Route path="/paywall" element={<Paywall />} /> <Route path="/paywall" element={<Paywall />} />
<Route path="/verify" element={<Verify />} /> <Route path="/verify" element={<Verify />} />
<Route path="/privacy" element={<PrivacyPolicy />} />
<Route path="/terms" element={<TermsOfService />} />
{/* Authenticated routes */} {/* Authenticated routes */}
{isAuthenticated && ( {isAuthenticated && (
@ -874,6 +882,22 @@ const cancelLogout = () => {
</Routes> </Routes>
</main> </main>
{/* Minimal global footer with legal links (always visible) */}
<footer className="border-t bg-white">
<div className="mx-auto max-w-6xl px-4 py-3 text-center">
<span className="text-xs text-gray-500">
© {new Date().getFullYear()} AptivaAI<span className="align-super text-sm"></span> LLC ·{' '}
<Link to="/privacy" className="text-aptiva hover:underline">
Privacy Policy
</Link>{' '}
·{' '}
<Link to="/terms" className="text-aptiva hover:underline">
Terms of Service
</Link>
</span>
</div>
</footer>
{/* Support modal mounted once at root so it centers correctly on desktop & mobile */} {/* Support modal mounted once at root so it centers correctly on desktop & mobile */}
<SupportModal <SupportModal
open={supportOpen} open={supportOpen}

View File

@ -0,0 +1,129 @@
import React, { useState } from 'react';
function EmailReveal({ className = '' }) {
const [shown, setShown] = useState(false);
const [copied, setCopied] = useState(false);
// Assemble at runtime; dont render the address until user action
const u = 'support';
const d = 'aptivaai.com';
const addr = `${u}@${d}`;
const onReveal = () => { setShown(true); };
const onCopy = async () => {
try {
await navigator.clipboard.writeText(addr);
setCopied(true);
setTimeout(() => setCopied(false), 1500);
} catch {}
};
return (
<span className={className}>
{!shown ? (
<button type="button" onClick={onReveal} className="text-aptiva underline">
Click to reveal email
</button>
) : (
<button type="button" onClick={onCopy} className="text-aptiva underline">
{copied ? 'Copied!' : addr}
</button>
)}
</span>
);
}
function PrivacyPolicy() {
return (
<div className="min-h-screen bg-gray-50 py-10 px-4">
<div className="mx-auto max-w-3xl rounded-xl bg-white p-6 shadow-lg border border-gray-200">
<h1 className="text-3xl font-bold mb-4 text-aptiva">Privacy Policy</h1>
<p className="text-sm text-gray-500 mb-6">
Effective Date: 9/19/25
</p>
<h2 className="text-xl font-semibold mb-2">Introduction</h2>
<p className="mb-4 text-gray-700">
AptivaAI LLC (AptivaAI, we, our, or us) respects your privacy.
This Privacy Policy explains what information we collect, how we use it,
and your rights.
</p>
<h2 className="text-xl font-semibold mb-2">Information We Collect</h2>
<ul className="list-disc list-inside mb-4 text-gray-700">
<li>Account information (username, password, profile details).</li>
<li>
Payment information handled by third-party <strong>payment processors</strong>
+ (we do not store full card details).
</li>
<li>
Messaging information (phone/email for notifications) handled by <strong>communications providers</strong>.
</li>
<li>Career and education data you enter into your profile.</li>
<li>
Technical information: necessary cookies and local storage (for
authentication, security, and preferences).
</li>
</ul>
<p className="mb-4 text-gray-700">
We do <strong>not</strong> use third-party analytics or marketing
cookies at this time. If we add them later, youll be able to opt in
before theyre used.
</p>
<h2 className="text-xl font-semibold mb-2">How We Use Information</h2>
<ul className="list-disc list-inside mb-4 text-gray-700">
<li>Provide and improve AptivaAIs services.</li>
<li>Process subscription payments.</li>
<li>Securely authenticate your account.</li>
<li>Deliver optional notifications (if enabled).</li>
</ul>
<p className="mb-4 text-gray-700">
We do not sell your personal information.
</p>
<h2 className="text-xl font-semibold mb-2">Cookies and Local Storage</h2>
<p className="mb-4 text-gray-700">
Necessary cookies/local storage are always used for authentication and
site functionality. Optional cookies (analytics, marketing) are not
currently in use. If we add them later, well update this policy and
request your consent.
</p>
<h2 className="text-xl font-semibold mb-2">Data Sharing</h2>
<p className="mb-4 text-gray-700">
We only share information with providers required to run AptivaAI.
These providers are bound by their
own security/privacy obligations. We do not sell or rent your data.
</p>
<h2 className="text-xl font-semibold mb-2">Data Security</h2>
<p className="mb-4 text-gray-700">
We take security seriously with encryption, strict access controls, and
logging. While no system is 100% secure, we continually work to protect
your data.
</p>
<h2 className="text-xl font-semibold mb-2">Your Rights</h2>
<p className="mb-4 text-gray-700">
Depending on your location, you may have the right to access, correct,
or delete your personal data; opt out of optional cookies (when added);
or cancel your account. To exercise your rights, contact us at{' '}
<EmailReveal className="ml-1" />.
</p>
<h2 className="text-xl font-semibold mb-2">Updates</h2>
<p className="mb-4 text-gray-700">
We may update this Privacy Policy from time to time. If we make
significant changes, well notify you in the app or by email.
</p>
<h2 className="text-xl font-semibold mb-2">Contact</h2>
<p className="mb-2 text-gray-700">
AptivaAI LLC <br />
Email: <EmailReveal className="ml-1" />
</p>
</div>
</div>
);
}
export default PrivacyPolicy;

View File

@ -10,6 +10,7 @@ function SignIn({ setIsAuthenticated, setUser }) {
const passwordRef = useRef(''); const passwordRef = useRef('');
const [error, setError] = useState(''); const [error, setError] = useState('');
const [showSessionExpiredMsg, setShowSessionExpiredMsg] = useState(false); const [showSessionExpiredMsg, setShowSessionExpiredMsg] = useState(false);
const [showConsent, setShowConsent] = useState(false);
const location = useLocation(); const location = useLocation();
useEffect(() => { useEffect(() => {
@ -19,6 +20,33 @@ function SignIn({ setIsAuthenticated, setUser }) {
} }
}, [location.search]); }, [location.search]);
// ────────────────────────────────────────────────────────────
// Consent banner (opt-in; necessary only always on)
// Stored in safeLocal under 'cookieConsent'
// ────────────────────────────────────────────────────────────
useEffect(() => {
const existing = safeLocal.getItem('cookieConsent'); // {version, necessary, ts}
if (!existing) {
setShowConsent(true);
} else {
// expose globally for any script that checks it
window.__aptivaConsent = existing;
}
}, []);
const saveConsent = () => {
const payload = {
version: 1,
necessary: true,
ts: Date.now(),
};
safeLocal.setItem('cookieConsent', payload);
window.__aptivaConsent = payload; // allow other modules to read without storage hit
setShowConsent(false);
};
const handleSignIn = async (event) => { const handleSignIn = async (event) => {
event.preventDefault(); event.preventDefault();
setError(''); setError('');
@ -92,7 +120,7 @@ function SignIn({ setIsAuthenticated, setUser }) {
)} )}
<div className="w-full max-w-sm rounded-xl bg-white p-6 shadow-lg border border-gray-100"> <div className="w-full max-w-sm rounded-xl bg-white p-6 shadow-lg border border-gray-100">
{/* Wordmark (text-only) */} {/* Wordmark (text-only) */}
<div className="mb-1 text-center text-aptiva font-semibold tracking-tight">AptivaAI</div> <div className="mb-1 text-center text-aptiva font-semibold tracking-tight">AptivaAI<span className="align-super text-sm"></span></div>
<h1 className="mb-1 text-center text-2xl font-semibold">Sign In</h1> <h1 className="mb-1 text-center text-2xl font-semibold">Sign In</h1>
<p className="mb-6 text-center text-xs text-gray-500">Career guidance powered by data enhanced by AI</p> <p className="mb-6 text-center text-xs text-gray-500">Career guidance powered by data enhanced by AI</p>
@ -143,6 +171,44 @@ function SignIn({ setIsAuthenticated, setUser }) {
</div> </div>
</div> </div>
</div> </div>
{/* ───────────────── Cookie Consent Banner (SignIn only) ───────────────── */}
{showConsent && (
<div className="fixed inset-x-0 bottom-0 z-50">
<div className="mx-auto mb-4 w-[95%] max-w-3xl rounded-xl border border-gray-200 bg-white shadow-xl">
<div className="p-4">
<h2 className="text-base font-semibold mb-1">Cookies & Privacy</h2>
<p className="text-sm text-gray-600">
We only use <span className="font-medium">necessary</span> cookies to run AptivaAI<span className="align-super text-sm"></span>
(session/auth and basic preferences). By continuing, you consent to these necessary cookies.
</p>
<div className="mt-3 flex flex-wrap gap-2">
<button
onClick={saveConsent}
className="rounded-lg bg-aptiva px-4 py-2 text-white hover:bg-aptiva-dark"
>
OK
</button>
<div className="ml-auto flex gap-4">
<Link
to="/privacy"
className="text-sm text-aptiva hover:underline"
>
Privacy Policy
</Link>
<Link
to="/terms"
className="text-sm text-aptiva hover:underline"
>
Terms of Service
</Link>
</div>
</div>
</div>
</div>
</div>
)}
{/* ─────────────────────────────────────────────────────────────────────── */}
</div> </div>
); );
} }

View File

@ -0,0 +1,117 @@
import React, { useState } from 'react';
function EmailReveal({ className = '' }) {
const [shown, setShown] = useState(false);
const [copied, setCopied] = useState(false);
const u = 'support';
const d = 'aptivaai.com';
const addr = `${u}@${d}`;
const onReveal = () => { setShown(true); };
const onCopy = async () => {
try {
await navigator.clipboard.writeText(addr);
setCopied(true);
setTimeout(() => setCopied(false), 1500);
} catch {}
};
return (
<span className={className}>
{!shown ? (
<button type="button" onClick={onReveal} className="text-aptiva underline">
Click to reveal email
</button>
) : (
<button type="button" onClick={onCopy} className="text-aptiva underline">
{copied ? 'Copied!' : addr}
</button>
)}
</span>
);
}
function TermsOfService() {
return (
<div className="min-h-screen bg-gray-50 py-10 px-4">
<div className="mx-auto max-w-3xl rounded-xl bg-white p-6 shadow-lg border border-gray-200">
<h1 className="text-3xl font-bold mb-4 text-aptiva">Terms of Service</h1>
<p className="text-sm text-gray-500 mb-6">Effective Date: 9/19/25 </p>
<h2 className="text-xl font-semibold mb-2">1. Acceptance of Terms</h2>
<p className="mb-4 text-gray-700">
By accessing or using AptivaAI (Service), you agree to these Terms of Service (Terms).
If you do not agree, do not use the Service.
</p>
<h2 className="text-xl font-semibold mb-2">2. Eligibility</h2>
<p className="mb-4 text-gray-700">
You must be at least 16 years old, or the minimum age required by law in your jurisdiction,
to use AptivaAI. By using the Service, you represent that you meet this requirement.
</p>
<h2 className="text-xl font-semibold mb-2">3. Accounts</h2>
<p className="mb-4 text-gray-700">
You are responsible for maintaining the confidentiality of your account credentials and for
all activity under your account. Notify us immediately of any unauthorized use.
</p>
<h2 className="text-xl font-semibold mb-2">4. Subscriptions & Payments</h2>
<p className="mb-4 text-gray-700">
Some features require a paid subscription. Payments are processed securely by
<strong> third-party payment processors</strong>. Fees are non-refundable except as required by law.
</p>
<h2 className="text-xl font-semibold mb-2">5. Acceptable Use</h2>
<p className="mb-4 text-gray-700">
You agree not to misuse the Service, including but not limited to: attempting to disrupt or
overload our systems; scraping or reverse engineering; impersonating others; or submitting
false or misleading information.
</p>
<h2 className="text-xl font-semibold mb-2">6. Intellectual Property</h2>
<p className="mb-4 text-gray-700">
AptivaAI content, trademarks, and software are owned by AptivaAI LLC. You may not copy,
modify, or distribute our materials without written permission.
</p>
<h2 className="text-xl font-semibold mb-2">7. Disclaimers</h2>
<p className="mb-4 text-gray-700">
AptivaAI provides career guidance and financial planning tools for informational purposes.
We do not guarantee employment outcomes, admissions, or financial results. The Service is
provided as is without warranties of any kind.
</p>
<h2 className="text-xl font-semibold mb-2">8. Limitation of Liability</h2>
<p className="mb-4 text-gray-700">
To the fullest extent permitted by law, AptivaAI LLC is not liable for any indirect,
incidental, or consequential damages arising from your use of the Service.
</p>
<h2 className="text-xl font-semibold mb-2">9. Termination</h2>
<p className="mb-4 text-gray-700">
We may suspend or terminate your account if you violate these Terms or misuse the Service.
You may cancel your account at any time through your settings.
</p>
<h2 className="text-xl font-semibold mb-2">10. Governing Law</h2>
<p className="mb-4 text-gray-700">
These Terms are governed by the laws of the State of Georgia, USA, without regard to
conflict of laws. Disputes will be resolved in courts located in Cobb County, Georgia.
</p>
<h2 className="text-xl font-semibold mb-2">11. Changes to Terms</h2>
<p className="mb-4 text-gray-700">
We may update these Terms from time to time. If changes are material, we will notify you
through the Service or by email. Continued use means you accept the updated Terms.
</p>
<h2 className="text-xl font-semibold mb-2">12. Contact Us</h2>
<p className="mb-2 text-gray-700">
AptivaAI LLC <br />
Email: <EmailReveal className="ml-1" />
</p>
</div>
</div>
);
}
export default TermsOfService;

View File

@ -8,6 +8,7 @@ const ALLOW = {
lastPanel: 1 * 24 * 3600 * 1000, // 1d lastPanel: 1 * 24 * 3600 * 1000, // 1d
flagsVersion: 6 * 3600 * 1000, // 6h flagsVersion: 6 * 3600 * 1000, // 6h
lastCareerName: 1 * 24 * 3600 * 1000, // 1d lastCareerName: 1 * 24 * 3600 * 1000, // 1d
cookieConsent: 365 * 24 * 3600 * 1000, // 1 year
}; };
function _ns(k) { return `${NS}${k}`; } function _ns(k) { return `${NS}${k}`; }