Removed stray documentation from repo and added the /demo endpoint
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
e6f98f9653
commit
6a58f62075
@ -1 +1 @@
|
||||
ec484f55e8a48bee6d0e6223d06b815335ea5e74-006fe5ea7fd8cb46290bcd0cf210f88f2ec04061-e9eccd451b778829eb2f2c9752c670b707e1268b
|
||||
7525e7b74f06b3341cb73a157afaea13b4af1f5d-006fe5ea7fd8cb46290bcd0cf210f88f2ec04061-e9eccd451b778829eb2f2c9752c670b707e1268b
|
||||
|
||||
@ -37,3 +37,13 @@ test-results/
|
||||
blob-report/
|
||||
*.trace.zip
|
||||
|
||||
# Conference and business planning documents (not needed in containers)
|
||||
COMPETITIVE_ANALYSIS.md
|
||||
PRICING_OPERATIONS_ANALYSIS.md
|
||||
INFRASTRUCTURE_SCALING_ANALYSIS.md
|
||||
COST_PROJECTION_DATA_NEEDED.md
|
||||
ACCURATE_COST_PROJECTIONS.md
|
||||
GAETC_PRINT_MATERIALS_FINAL.md
|
||||
CONFERENCE_MATERIALS.md
|
||||
APTIVA_AI_FEATURES_DOCUMENTATION.md
|
||||
|
||||
|
||||
10
.gitignore
vendored
10
.gitignore
vendored
@ -28,3 +28,13 @@ uploads/.env
|
||||
scan-env.sh
|
||||
.aptiva-test-user.json
|
||||
APTIVA_AI_FEATURES_DOCUMENTATION.md
|
||||
|
||||
# Conference and business planning documents (sensitive)
|
||||
COMPETITIVE_ANALYSIS.md
|
||||
PRICING_OPERATIONS_ANALYSIS.md
|
||||
INFRASTRUCTURE_SCALING_ANALYSIS.md
|
||||
COST_PROJECTION_DATA_NEEDED.md
|
||||
ACCURATE_COST_PROJECTIONS.md
|
||||
GAETC_PRINT_MATERIALS_FINAL.md
|
||||
CONFERENCE_MATERIALS.md
|
||||
APTIVA_AI_FEATURES_DOCUMENTATION.md
|
||||
|
||||
@ -1734,6 +1734,86 @@ ${body}`;
|
||||
}
|
||||
);
|
||||
|
||||
/* ----------------- Demo Request (Conference Lead Capture) ----------------- */
|
||||
|
||||
app.post('/api/demo-request', async (req, res) => {
|
||||
try {
|
||||
const { name, email, organization_name, phone, message } = req.body;
|
||||
|
||||
// Validation
|
||||
if (!name || !email || !organization_name) {
|
||||
return res.status(400).json({
|
||||
error: 'Name, email, and organization name are required'
|
||||
});
|
||||
}
|
||||
|
||||
// Email validation
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailRegex.test(email)) {
|
||||
return res.status(400).json({ error: 'Invalid email format' });
|
||||
}
|
||||
|
||||
// Insert to database (fields will be encrypted via withEncryption wrapper)
|
||||
const query = `
|
||||
INSERT INTO demo_requests
|
||||
(name, email, organization_name, phone, message, source)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
`;
|
||||
|
||||
const source = 'gaetc_nov2025'; // TODO: Update this before each conference
|
||||
|
||||
await pool.execute(query, [
|
||||
name.trim(),
|
||||
email.trim().toLowerCase(),
|
||||
organization_name.trim(),
|
||||
phone?.trim() || null,
|
||||
message?.trim() || null,
|
||||
source
|
||||
]);
|
||||
|
||||
// Send email notification
|
||||
if (SENDGRID_KEY) {
|
||||
try {
|
||||
const emailBody = `New demo request received:
|
||||
|
||||
Name: ${name}
|
||||
Email: ${email}
|
||||
Organization: ${organization_name}
|
||||
Phone: ${phone || 'Not provided'}
|
||||
Message: ${message || 'None'}
|
||||
|
||||
Source: ${source}
|
||||
Submitted: ${new Date().toLocaleString('en-US', { timeZone: 'America/New_York' })}
|
||||
|
||||
---
|
||||
Reply directly to ${email} to follow up.`;
|
||||
|
||||
await sgMail.send({
|
||||
to: 'jcoakley@aptivaai.com',
|
||||
from: 'noreply@aptivaai.com',
|
||||
replyTo: email,
|
||||
subject: `New Demo Request: ${organization_name}`,
|
||||
text: emailBody,
|
||||
html: `<pre style="font-family: ui-monospace, Menlo, monospace; white-space: pre-wrap">${emailBody}</pre>`,
|
||||
categories: ['demo-request', source]
|
||||
});
|
||||
} catch (emailErr) {
|
||||
console.error('[demo-request] Email notification failed:', emailErr?.message || emailErr);
|
||||
// Don't fail the request if email fails - data is still saved
|
||||
}
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Demo request received! We\'ll reach out soon to schedule a time that works for you.'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('[demo-request] Error:', error?.message || error);
|
||||
res.status(500).json({ error: 'Failed to submit request. Please try again.' });
|
||||
}
|
||||
});
|
||||
|
||||
/* ----------------- Support bot chat (server2) ----------------- */
|
||||
|
||||
/* CREATE thread */
|
||||
|
||||
@ -5043,7 +5043,7 @@ app.post(
|
||||
userPlan = 'premium';
|
||||
}
|
||||
|
||||
const weeklyLimits = { premium: 3, pro: 5 };
|
||||
const weeklyLimits = { premium: 10, pro: 10 };
|
||||
const userWeeklyLimit = weeklyLimits[userPlan] || 0;
|
||||
|
||||
let resetDate = new Date(userProfile.resume_limit_reset);
|
||||
@ -5143,7 +5143,7 @@ app.get('/api/premium/resume/remaining', authenticatePremiumUser, async (req, re
|
||||
userPlan = 'premium';
|
||||
}
|
||||
|
||||
const weeklyLimits = { basic: 1, premium: 2, pro: 5 };
|
||||
const weeklyLimits = { basic: 0, premium: 10, pro: 10 };
|
||||
const userWeeklyLimit = weeklyLimits[userPlan] || 0;
|
||||
|
||||
let resetDate = new Date(userProfile.resume_limit_reset);
|
||||
|
||||
@ -266,3 +266,16 @@ ALTER TABLE career_profiles
|
||||
ADD COLUMN resume_filesize INT UNSIGNED NULL AFTER resume_filename,
|
||||
ADD COLUMN resume_uploaded_at DATETIME NULL AFTER resume_filesize;
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS demo_requests (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(512) NOT NULL,
|
||||
email VARCHAR(512) NOT NULL,
|
||||
organization_name VARCHAR(512) NOT NULL,
|
||||
phone VARCHAR(512),
|
||||
message TEXT,
|
||||
source VARCHAR(512) DEFAULT 'conference',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
INDEX idx_created (created_at),
|
||||
INDEX idx_source (source(255))
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
10
nginx.conf
10
nginx.conf
@ -191,10 +191,14 @@ http {
|
||||
proxy_set_header Connection "";
|
||||
}
|
||||
|
||||
location = /api/user-profile { limit_conn perip 5;
|
||||
limit_req zone=reqperip burst=10 nodelay;
|
||||
location = /api/user-profile { limit_conn perip 5;
|
||||
limit_req zone=reqperip burst=10 nodelay;
|
||||
proxy_pass http://backend5000; }
|
||||
|
||||
|
||||
location = /api/demo-request { limit_conn perip 5;
|
||||
limit_req zone=reqperip burst=10 nodelay;
|
||||
proxy_pass http://backend5001; }
|
||||
|
||||
# General API (anything not matched above) – rate-limited
|
||||
location ^~ /api/ { proxy_pass http://backend5000; }
|
||||
|
||||
|
||||
10
src/App.js
10
src/App.js
@ -51,6 +51,7 @@ import { initNetObserver } from './utils/net.js';
|
||||
import PrivacyPolicy from './components/PrivacyPolicy.js';
|
||||
import TermsOfService from './components/TermsOfService.js';
|
||||
import HomePage from './components/HomePage.js';
|
||||
import DemoRequest from './components/DemoRequest.js';
|
||||
|
||||
|
||||
|
||||
@ -231,7 +232,8 @@ if (loggingOut) return;
|
||||
location.pathname === '/forgot-password' ||
|
||||
location.pathname === '/privacy' ||
|
||||
location.pathname === '/terms' ||
|
||||
location.pathname === '/home'
|
||||
location.pathname === '/home' ||
|
||||
location.pathname === '/demo'
|
||||
) {
|
||||
try { localStorage.removeItem('id'); } catch {}
|
||||
setIsAuthenticated(false);
|
||||
@ -269,7 +271,8 @@ if (loggingOut) return;
|
||||
p === '/paywall' ||
|
||||
p === '/privacy' ||
|
||||
p === '/terms' ||
|
||||
p === '/home';
|
||||
p === '/home' ||
|
||||
p === '/demo';
|
||||
if (!onPublic) navigate('/signin?session=expired', { replace: true });
|
||||
} finally {
|
||||
if (!cancelled) setIsLoading(false);
|
||||
@ -810,6 +813,9 @@ const cancelLogout = () => {
|
||||
{/* Public Home Page */}
|
||||
<Route path="/home" element={<HomePage />} />
|
||||
|
||||
{/* Public Demo Request (Conference Lead Capture) */}
|
||||
<Route path="/demo" element={<DemoRequest />} />
|
||||
|
||||
{/* Default */}
|
||||
<Route
|
||||
path="/"
|
||||
|
||||
216
src/components/DemoRequest.js
Normal file
216
src/components/DemoRequest.js
Normal file
@ -0,0 +1,216 @@
|
||||
import { useState } from 'react';
|
||||
import axios from 'axios';
|
||||
|
||||
const DemoRequest = () => {
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
email: '',
|
||||
organization_name: '',
|
||||
phone: '',
|
||||
message: "I'd like to schedule a demo"
|
||||
});
|
||||
const [submitted, setSubmitted] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const handleChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
[name]: value
|
||||
}));
|
||||
// Clear error when user starts typing
|
||||
if (error) setError('');
|
||||
};
|
||||
|
||||
const validateForm = () => {
|
||||
if (!formData.name.trim()) {
|
||||
setError('Please enter your name');
|
||||
return false;
|
||||
}
|
||||
if (!formData.email.trim()) {
|
||||
setError('Please enter your email');
|
||||
return false;
|
||||
}
|
||||
// Email format validation
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailRegex.test(formData.email)) {
|
||||
setError('Please enter a valid email address');
|
||||
return false;
|
||||
}
|
||||
if (!formData.organization_name.trim()) {
|
||||
setError('Please enter your organization name');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (!validateForm()) return;
|
||||
|
||||
setLoading(true);
|
||||
setError('');
|
||||
|
||||
try {
|
||||
const response = await axios.post('/api/demo-request', formData);
|
||||
|
||||
if (response.data.success) {
|
||||
setSubmitted(true);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Demo request error:', err);
|
||||
setError(
|
||||
err.response?.data?.error ||
|
||||
'Failed to submit request. Please try again or email us directly.'
|
||||
);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (submitted) {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center p-5 bg-gradient-to-br from-indigo-500 via-purple-500 to-pink-500">
|
||||
<div className="bg-white rounded-xl shadow-2xl max-w-md w-full p-12 text-center">
|
||||
<div className="w-20 h-20 bg-gradient-to-br from-green-500 to-green-600 text-white text-5xl rounded-full flex items-center justify-center mx-auto mb-6 animate-[scaleIn_0.5s_ease-out]">
|
||||
✓
|
||||
</div>
|
||||
<h2 className="text-2xl font-semibold text-gray-900 mb-4">Request Received!</h2>
|
||||
<p className="text-base text-gray-600 leading-relaxed mb-8">
|
||||
We've received your demo request and will reach out soon to schedule a time that works for you.
|
||||
</p>
|
||||
<div className="pt-6 border-t border-gray-200">
|
||||
<p className="text-sm text-gray-500 mb-3">Want to connect sooner?</p>
|
||||
<a
|
||||
href={`mailto:jcoakley@aptivaai.com?subject=Demo Request: ${formData.organization_name}`}
|
||||
className="inline-block bg-gradient-to-r from-indigo-500 to-purple-600 text-white px-6 py-3 rounded-lg font-semibold text-sm transition-all hover:-translate-y-0.5 hover:shadow-[0_6px_20px_rgba(102,126,234,0.4)]"
|
||||
>
|
||||
Email jcoakley@aptivaai.com directly
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center p-5 bg-gradient-to-br from-indigo-500 via-purple-500 to-pink-500">
|
||||
<div className="bg-white rounded-xl shadow-2xl max-w-lg w-full p-10">
|
||||
<div className="text-center mb-8">
|
||||
<h1 className="text-3xl font-semibold text-gray-900 mb-2">Schedule a Demo</h1>
|
||||
<p className="text-base text-gray-500">Career Planning for the AI Era</p>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className="flex flex-col gap-5">
|
||||
<div className="flex flex-col">
|
||||
<label htmlFor="name" className="text-sm font-medium text-gray-700 mb-1.5">
|
||||
Name <span className="text-red-600">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
name="name"
|
||||
value={formData.name}
|
||||
onChange={handleChange}
|
||||
placeholder="Your full name"
|
||||
disabled={loading}
|
||||
autoComplete="name"
|
||||
className="px-3.5 py-3 text-base border-2 border-gray-200 rounded-lg transition-all focus:outline-none focus:border-indigo-500 focus:ring-4 focus:ring-indigo-100 disabled:bg-gray-50 disabled:cursor-not-allowed"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col">
|
||||
<label htmlFor="email" className="text-sm font-medium text-gray-700 mb-1.5">
|
||||
Email <span className="text-red-600">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
name="email"
|
||||
value={formData.email}
|
||||
onChange={handleChange}
|
||||
placeholder="you@organization.edu"
|
||||
disabled={loading}
|
||||
autoComplete="email"
|
||||
className="px-3.5 py-3 text-base border-2 border-gray-200 rounded-lg transition-all focus:outline-none focus:border-indigo-500 focus:ring-4 focus:ring-indigo-100 disabled:bg-gray-50 disabled:cursor-not-allowed"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col">
|
||||
<label htmlFor="organization_name" className="text-sm font-medium text-gray-700 mb-1.5">
|
||||
Organization <span className="text-red-600">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="organization_name"
|
||||
name="organization_name"
|
||||
value={formData.organization_name}
|
||||
onChange={handleChange}
|
||||
placeholder="School, district, or college name"
|
||||
disabled={loading}
|
||||
autoComplete="organization"
|
||||
className="px-3.5 py-3 text-base border-2 border-gray-200 rounded-lg transition-all focus:outline-none focus:border-indigo-500 focus:ring-4 focus:ring-indigo-100 disabled:bg-gray-50 disabled:cursor-not-allowed"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col">
|
||||
<label htmlFor="phone" className="text-sm font-medium text-gray-700 mb-1.5">
|
||||
Phone (optional)
|
||||
</label>
|
||||
<input
|
||||
type="tel"
|
||||
id="phone"
|
||||
name="phone"
|
||||
value={formData.phone}
|
||||
onChange={handleChange}
|
||||
placeholder="(555) 123-4567"
|
||||
disabled={loading}
|
||||
autoComplete="tel"
|
||||
className="px-3.5 py-3 text-base border-2 border-gray-200 rounded-lg transition-all focus:outline-none focus:border-indigo-500 focus:ring-4 focus:ring-indigo-100 disabled:bg-gray-50 disabled:cursor-not-allowed"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col">
|
||||
<label htmlFor="message" className="text-sm font-medium text-gray-700 mb-1.5">
|
||||
Message (optional)
|
||||
</label>
|
||||
<textarea
|
||||
id="message"
|
||||
name="message"
|
||||
value={formData.message}
|
||||
onChange={handleChange}
|
||||
rows="3"
|
||||
disabled={loading}
|
||||
placeholder="Tell us about your needs..."
|
||||
className="px-3.5 py-3 text-base border-2 border-gray-200 rounded-lg transition-all focus:outline-none focus:border-indigo-500 focus:ring-4 focus:ring-indigo-100 disabled:bg-gray-50 disabled:cursor-not-allowed resize-y min-h-[80px]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<div className="bg-red-50 text-red-700 px-4 py-3 rounded-lg border-l-4 border-red-500 text-sm">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
className="bg-gradient-to-r from-indigo-500 to-purple-600 text-white px-6 py-3.5 text-base font-semibold rounded-lg transition-all mt-2.5 hover:-translate-y-0.5 hover:shadow-[0_6px_20px_rgba(102,126,234,0.4)] active:translate-y-0 disabled:opacity-60 disabled:cursor-not-allowed"
|
||||
disabled={loading}
|
||||
>
|
||||
{loading ? 'Submitting...' : 'Request Demo'}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div className="mt-5 text-center">
|
||||
<p className="text-xs text-gray-500 leading-relaxed">
|
||||
We respect your privacy. Your information will only be used to contact you about Aptiva AI.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DemoRequest;
|
||||
@ -38,7 +38,7 @@ export default function HomePage() {
|
||||
</Button>
|
||||
</div>
|
||||
<p className="text-sm text-gray-500 mt-4">
|
||||
Free forever · No credit card required · Full access to career planning tools
|
||||
No credit card required · Full access to career planning tools
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
@ -201,7 +201,7 @@ export default function HomePage() {
|
||||
<svg className="w-5 h-5 text-green-500 mr-2 flex-shrink-0 mt-0.5" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" />
|
||||
</svg>
|
||||
<span>AI Career Coach powered by GPT-4o for personalized milestone recommendations</span>
|
||||
<span>AI Career Coach Agent for personalized milestone recommendations and automated milestone/task/impact creation</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<svg className="w-5 h-5 text-green-500 mr-2 flex-shrink-0 mt-0.5" fill="currentColor" viewBox="0 0 20 20">
|
||||
@ -213,7 +213,7 @@ export default function HomePage() {
|
||||
<svg className="w-5 h-5 text-green-500 mr-2 flex-shrink-0 mt-0.5" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" />
|
||||
</svg>
|
||||
<span>Resume optimizer tailored to specific job descriptions (2-5/week)</span>
|
||||
<span>Resume optimizer tailored to specific job descriptions (10/week)</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@ -311,13 +311,13 @@ export default function HomePage() {
|
||||
<div className="text-center">
|
||||
<div className="bg-aptiva/10 rounded-full w-16 h-16 flex items-center justify-center mx-auto mb-4">
|
||||
<svg className="w-8 h-8 text-aptiva" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 className="text-xl font-bold text-gray-900 mb-2">Affordable</h3>
|
||||
<h3 className="text-xl font-bold text-gray-900 mb-2">Privacy-First</h3>
|
||||
<p className="text-gray-600">
|
||||
Premium features start at just $4.99/month—less than a coffee per week.
|
||||
Free tier includes comprehensive career exploration tools.
|
||||
We never ask for your birthdate or age. Plan your career and retirement
|
||||
without giving up personal data. Your privacy is protected.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -439,7 +439,7 @@ export default function HomePage() {
|
||||
<svg className="w-5 h-5 text-green-500 mr-2 flex-shrink-0 mt-0.5" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" />
|
||||
</svg>
|
||||
<span>AI Resume optimizer (2 per week)</span>
|
||||
<span>AI Resume optimizer (10 per week)</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<svg className="w-5 h-5 text-green-500 mr-2 flex-shrink-0 mt-0.5" fill="currentColor" viewBox="0 0 20 20">
|
||||
|
||||
@ -118,7 +118,7 @@ export default function Paywall() {
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<span className="text-green-600 font-bold mt-0.5">✓</span>
|
||||
<span><strong>Resume Optimizer</strong> – 3 AI-enhanced optimizations per week</span>
|
||||
<span><strong>Resume Optimizer</strong> – 10 AI-enhanced optimizations per week</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<span className="text-green-600 font-bold mt-0.5">✓</span>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user