Legal, TM, ToS, Privacy Policy
All checks were successful
ci/woodpecker/manual/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/manual/woodpecker Pipeline was successful
This commit is contained in:
parent
bbde9f2da2
commit
caa78298ec
@ -1 +1 @@
|
||||
be65400be96a473622c09b3df6073c5837dacc82-372bcf506971f56c4911b429b9f5de5bc37ed008-e9eccd451b778829eb2f2c9752c670b707e1268b
|
||||
c5704005f291ecf264cdc92403119e7db5831e61-372bcf506971f56c4911b429b9f5de5bc37ed008-e9eccd451b778829eb2f2c9752c670b707e1268b
|
||||
|
30
src/App.js
30
src/App.js
@ -48,6 +48,8 @@ import * as safeLocal from './utils/safeLocal.js';
|
||||
import VerificationGate from './components/VerificationGate.js';
|
||||
import Verify from './components/Verify.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 === '/signin' ||
|
||||
location.pathname === '/signup' ||
|
||||
location.pathname === '/forgot-password'
|
||||
location.pathname === '/forgot-password' ||
|
||||
location.pathname === '/privacy' ||
|
||||
location.pathname === '/terms'
|
||||
) {
|
||||
try { localStorage.removeItem('id'); } catch {}
|
||||
setIsAuthenticated(false);
|
||||
@ -260,7 +264,9 @@ if (loggingOut) return;
|
||||
p === '/signup' ||
|
||||
p === '/forgot-password' ||
|
||||
p.startsWith('/reset-password') ||
|
||||
p === '/paywall';
|
||||
p === '/paywall' ||
|
||||
p === '/privacy' ||
|
||||
p === '/terms';
|
||||
if (!onPublic) navigate('/signin?session=expired', { replace: true });
|
||||
} finally {
|
||||
if (!cancelled) setIsLoading(false);
|
||||
@ -372,7 +378,7 @@ const cancelLogout = () => {
|
||||
{/* 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">
|
||||
<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>
|
||||
|
||||
{/* Mobile hamburger */}
|
||||
@ -836,6 +842,8 @@ const cancelLogout = () => {
|
||||
|
||||
<Route path="/paywall" element={<Paywall />} />
|
||||
<Route path="/verify" element={<Verify />} />
|
||||
<Route path="/privacy" element={<PrivacyPolicy />} />
|
||||
<Route path="/terms" element={<TermsOfService />} />
|
||||
|
||||
{/* Authenticated routes */}
|
||||
{isAuthenticated && (
|
||||
@ -874,6 +882,22 @@ const cancelLogout = () => {
|
||||
</Routes>
|
||||
</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 */}
|
||||
<SupportModal
|
||||
open={supportOpen}
|
||||
|
129
src/components/PrivacyPolicy.js
Normal file
129
src/components/PrivacyPolicy.js
Normal 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; don’t 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, you’ll be able to opt in
|
||||
before they’re 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 AptivaAI’s 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, we’ll 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, we’ll 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;
|
@ -10,6 +10,7 @@ function SignIn({ setIsAuthenticated, setUser }) {
|
||||
const passwordRef = useRef('');
|
||||
const [error, setError] = useState('');
|
||||
const [showSessionExpiredMsg, setShowSessionExpiredMsg] = useState(false);
|
||||
const [showConsent, setShowConsent] = useState(false);
|
||||
const location = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
@ -19,6 +20,33 @@ function SignIn({ setIsAuthenticated, setUser }) {
|
||||
}
|
||||
}, [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) => {
|
||||
event.preventDefault();
|
||||
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">
|
||||
{/* 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>
|
||||
<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>
|
||||
{/* ───────────────── 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>
|
||||
);
|
||||
}
|
||||
|
117
src/components/TermsOfService.js
Normal file
117
src/components/TermsOfService.js
Normal 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;
|
@ -8,6 +8,7 @@ const ALLOW = {
|
||||
lastPanel: 1 * 24 * 3600 * 1000, // 1d
|
||||
flagsVersion: 6 * 3600 * 1000, // 6h
|
||||
lastCareerName: 1 * 24 * 3600 * 1000, // 1d
|
||||
cookieConsent: 365 * 24 * 3600 * 1000, // 1 year
|
||||
};
|
||||
|
||||
function _ns(k) { return `${NS}${k}`; }
|
||||
|
Loading…
Reference in New Issue
Block a user