Branding/UI & Loan Repayment fixes
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
5a0c7efd25
commit
e25662aae4
@ -1 +1 @@
|
||||
77b19da169acfc9f13cfa14f2fb425fea6c03ef4-372bcf506971f56c4911b429b9f5de5bc37ed008-e9eccd451b778829eb2f2c9752c670b707e1268b
|
||||
e520c3d4f21d892f230efe5f06e338b842191dd6-372bcf506971f56c4911b429b9f5de5bc37ed008-e9eccd451b778829eb2f2c9752c670b707e1268b
|
||||
|
@ -182,6 +182,7 @@ services:
|
||||
networks: [default, aptiva-shared]
|
||||
environment:
|
||||
GOOGLE_MAPS_API_KEY: ${GOOGLE_MAPS_API_KEY}
|
||||
ENV_NAME: ${ENV_NAME}
|
||||
ports: ["80:80", "443:443"]
|
||||
volumes:
|
||||
- ./nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
|
@ -442,6 +442,12 @@ const cancelLogout = () => {
|
||||
>
|
||||
Educational Programs
|
||||
</Link>
|
||||
<Link
|
||||
to="/preparing?loan=1"
|
||||
className="block px-4 py-2 hover:bg-gray-100 text-sm text-gray-700"
|
||||
>
|
||||
Education Repayment Calculator
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -689,6 +695,7 @@ const cancelLogout = () => {
|
||||
Preparing — Overview
|
||||
</Link>
|
||||
<Link to="/educational-programs" className="block px-2 py-2 text-sm text-gray-700 rounded hover:bg-gray-100">Educational Programs</Link>
|
||||
<Link to="/preparing?loan=1" className="block px-2 py-2 text-sm text-gray-700 rounded hover:bg-gray-100">Education Repayment Calculator</Link>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
@ -1,64 +1,60 @@
|
||||
import React from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Button } from "./ui/button.js";
|
||||
import EconomicProjections from "./EconomicProjections.js";
|
||||
|
||||
/* simple pill-style label */
|
||||
const Chip = ({ label }) => (
|
||||
<span
|
||||
className="
|
||||
inline-flex items-center text-sm px-3 py-1
|
||||
rounded-full bg-gray-100 text-gray-700
|
||||
border border-gray-300
|
||||
"
|
||||
>
|
||||
{label}
|
||||
<span className="ml-1 text-xs text-green-600">✓ in Career Coach</span>
|
||||
</span>
|
||||
);
|
||||
|
||||
export default function EnhancingLanding({ userProfile }) {
|
||||
const navigate = useNavigate();
|
||||
const socCode = userProfile?.socCode;
|
||||
const stateName = userProfile?.state;
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 py-8 px-4">
|
||||
<div className="max-w-4xl mx-auto space-y-10">
|
||||
|
||||
{/* 📌 Current status */}
|
||||
<section className="bg-white shadow rounded-lg p-6">
|
||||
<h2 className="text-2xl font-semibold mb-4">
|
||||
📌 Your Current Status & Next Steps
|
||||
</h2>
|
||||
<p className="text-gray-600 mb-4">
|
||||
Evaluate where you are in your career and discover upcoming milestones with our AI recommendations.
|
||||
<div className="min-h-screen bg-gray-50 py-10 px-4">
|
||||
<div className="max-w-3xl mx-auto bg-white rounded-2xl shadow p-6 md:p-8">
|
||||
{/* Title + intro (centered, blue headline) */}
|
||||
<h1 className="text-3xl font-bold text-blue-600 text-center">
|
||||
Enhancing Your Career
|
||||
</h1>
|
||||
<p className="text-center text-gray-600 mt-2">
|
||||
Build momentum in your current role and prepare for your next step.
|
||||
Use Career Coach for guided milestones, and fine-tune your materials
|
||||
with the Resume Optimizer.
|
||||
</p>
|
||||
|
||||
<EconomicProjections socCode={socCode} stateName={stateName} />
|
||||
|
||||
{/* Feature tiles (inside the main card) */}
|
||||
<div className="grid gap-6 md:gap-8 md:grid-cols-2 mt-6">
|
||||
{/* Career Coach */}
|
||||
<div className="rounded-xl border border-gray-200 p-5">
|
||||
<h3 className="text-lg font-medium">Career Coach</h3>
|
||||
<p className="text-gray-600 mt-1">
|
||||
Guided milestones for networking, interview prep, and job search.
|
||||
</p>
|
||||
<div className="mt-4">
|
||||
<Button onClick={() => navigate("/career-roadmap")}>
|
||||
<Button size="lg" onClick={() => navigate("/career-roadmap")}>
|
||||
Open Career Coach
|
||||
</Button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* 🚀 How do I get there */}
|
||||
<section className="bg-white shadow rounded-lg p-6">
|
||||
<h2 className="text-2xl font-semibold mb-4">🚀 How Do I Get There?</h2>
|
||||
|
||||
<div className="flex flex-wrap gap-3">
|
||||
<Chip label="Resume Optimizer" />
|
||||
<Chip label="Networking" />
|
||||
<Chip label="Interview Help" />
|
||||
<Chip label="Job Search" />
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-gray-500 mt-3">
|
||||
All of these tools live inside the <strong>Career Coach</strong>. Open it any time using the button above.
|
||||
{/* Resume Optimizer (separate tool) */}
|
||||
<div className="rounded-xl border border-gray-200 p-5">
|
||||
<h3 className="text-lg font-medium">Resume Optimizer</h3>
|
||||
<p className="text-gray-600 mt-1">
|
||||
Improve wording, keywords, and formatting to match target roles.
|
||||
</p>
|
||||
<div className="mt-4">
|
||||
<Button size="lg" onClick={() => navigate("/resume-optmizer")}>
|
||||
Open Resume Optimizer
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Divider + lower subsection to mirror the secondary blocks on other landings */}
|
||||
<hr className="my-6 border-gray-200" />
|
||||
|
||||
<h2 className="text-xl font-semibold">Your Current Status & Next Steps</h2>
|
||||
<p className="text-gray-600 mt-1">
|
||||
Evaluate where you are in your career and discover upcoming milestones
|
||||
with our AI recommendations.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
@ -6,6 +6,8 @@ import { Button } from './ui/button.js';
|
||||
import { getNearbySchools } from '../utils/getNearbySchools.js';
|
||||
import UpsellSummary from './UpsellSummary.js';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import api from '../auth/apiClient.js';
|
||||
|
||||
|
||||
/* ───────────────────────── CONSTANTS ───────────────────────── */
|
||||
const DEGREE_OPTS = [
|
||||
@ -32,9 +34,21 @@ export default function LoanRepaymentDrawer({
|
||||
}) {
|
||||
/* Hooks must always run – return null later if !open */
|
||||
/* ── Remote data for auto-suggest ─ */
|
||||
const [cipData, setCipData] = useState([]);
|
||||
const [schoolSearch, setSchoolSearch] = useState('');
|
||||
const [icData, setIcData] = useState([]);
|
||||
const [selectedSchool, setSelectedSchool] = useState({ name: '', unitId: null });
|
||||
const [schoolSug, setSchoolSug] = useState([]); // [{name, unitId}]
|
||||
const [schoolSugOpen, setSchoolSugOpen] = useState(false);
|
||||
const [schoolHi, setSchoolHi] = useState(-1);
|
||||
|
||||
// Program auto-suggest (depends on selectedSchool)
|
||||
const [programSearch, setProgramSearch] = useState('');
|
||||
const [selectedProgram, setSelectedProgram] = useState(''); // exact string
|
||||
const [programSug, setProgramSug] = useState([]); // [{program}]
|
||||
const [programSugOpen, setProgramSugOpen] = useState(false);
|
||||
const [programHi, setProgramHi] = useState(-1);
|
||||
|
||||
// Degree types (filtered by school + program)
|
||||
const [degreeTypes, setDegreeTypes] = useState([]); // ['Bachelor's Degree', ...]
|
||||
|
||||
/* ── Simple form fields ─ */
|
||||
const [degree, setDegree] = useState('');
|
||||
@ -62,106 +76,114 @@ const getAnnualTuition = (schoolsArr, typed, isGrad, resid) =>
|
||||
pickField(schoolsArr?.[0] ?? {}, isGrad, resid) ||
|
||||
(typed ? Number(typed) : 0);
|
||||
|
||||
// Degree menu: prefer backend-provided types (if any), else fall back
|
||||
const degreeMenu = degreeTypes.length ? degreeTypes : DEGREE_OPTS;
|
||||
|
||||
/* ── memo’ed degree list for current school ── */
|
||||
const schoolDegrees = useMemo(() => {
|
||||
if (!schoolSearch.trim()) return [];
|
||||
const list = cipData
|
||||
.filter(r => r.INSTNM.toLowerCase() === schoolSearch.toLowerCase())
|
||||
.map(r => r.CREDDESC);
|
||||
return [...new Set(list)];
|
||||
}, [schoolSearch, cipData]);
|
||||
|
||||
const degreeMenu = schoolDegrees.length ? schoolDegrees : DEGREE_OPTS;
|
||||
const isGrad = /(Master|Doctoral|First Professional|Graduate|Certificate)/i.test(degree);
|
||||
const annualTuition= getAnnualTuition(schools, tuition, isGrad, tuitionType);
|
||||
|
||||
const showUpsell = user && !user.is_premium && !user.is_pro_premium;
|
||||
|
||||
useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
if (schools.length) return;
|
||||
if (!cipCodes.length) return;
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
const seed = await getNearbySchools(cipCodes, userZip, userState);
|
||||
if (seed.length) setSchools(seed);
|
||||
} catch (e) {
|
||||
console.warn('auto-seed schools failed:', e);
|
||||
}
|
||||
} catch (e) { console.warn('auto-seed schools failed:', e); }
|
||||
})();
|
||||
}, [open, schools.length, cipCodes.join('-'), userZip, userState, setSchools]);
|
||||
|
||||
/* ════════════════════════════════════════════════════
|
||||
FETCH CIP DATA (only once the drawer is ever opened)
|
||||
════════════════════════════════════════════════════ */
|
||||
useEffect(() => {
|
||||
if (!open || cipData.length) return;
|
||||
fetch('/cip_institution_mapping_new.json')
|
||||
.then(r => r.text())
|
||||
.then(text =>
|
||||
text
|
||||
.split('\n')
|
||||
.map(l => { try { return JSON.parse(l); } catch { return null; } })
|
||||
.filter(Boolean)
|
||||
)
|
||||
.then(arr => setCipData(arr))
|
||||
.catch(e => console.error('CIP fetch error', e));
|
||||
}, [open, cipData.length]);
|
||||
/* ───────── SCHOOL SUGGEST (server) ───────── */
|
||||
|
||||
/* ════════════════════════════════════════════════════
|
||||
SCHOOL AUTOCOMPLETE LIST (memoised)
|
||||
════════════════════════════════════════════════════ */
|
||||
const suggestions = useMemo(() => {
|
||||
if (!schoolSearch.trim()) return [];
|
||||
const low = schoolSearch.toLowerCase();
|
||||
const set = new Set(
|
||||
cipData
|
||||
.filter(r => r.INSTNM.toLowerCase().includes(low))
|
||||
.map(r => r.INSTNM)
|
||||
);
|
||||
return [...set].slice(0, 10);
|
||||
}, [schoolSearch, cipData]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open || icData.length) return;
|
||||
fetch('/ic2023_ay.csv')
|
||||
.then(r => r.text())
|
||||
.then(text => {
|
||||
const [header, ...rows] = text.split('\n').map(l => l.split(','));
|
||||
return rows.map(row =>
|
||||
Object.fromEntries(row.map((v, i) => [header[i], v]))
|
||||
);
|
||||
})
|
||||
.then(setIcData)
|
||||
.catch(e => console.error('iPEDS load fail', e));
|
||||
}, [open, icData.length]);
|
||||
|
||||
/* ───────── auto-tuition when schoolSearch settles ───────── */
|
||||
useEffect(() => {
|
||||
if (!schoolSearch.trim() || !icData.length) return;
|
||||
const rec = cipData.find(r => r.INSTNM.toLowerCase() === schoolSearch.toLowerCase());
|
||||
if (!rec) return;
|
||||
const match = icData.find(r => r.UNITID === rec.UNITID);
|
||||
if (!match) return;
|
||||
|
||||
const calc = () => {
|
||||
const grad = /(Master|Doctoral|First Professional|Graduate|Certificate)/i.test(degree);
|
||||
if (!grad) {
|
||||
return tuitionType === 'inState'
|
||||
? parseFloat(match.TUITION1 || match.TUITION2 || '')
|
||||
: parseFloat(match.TUITION3 || '');
|
||||
if (!open) return;
|
||||
const q = schoolSearch.trim();
|
||||
if (q.length < 2) { setSchoolSug([]); return; }
|
||||
let cancelled = false;
|
||||
(async () => {
|
||||
try {
|
||||
const { data } = await api.get('/api/schools/suggest', {
|
||||
params: { query: q, limit: 10 },
|
||||
withCredentials: true,
|
||||
});
|
||||
if (!cancelled) setSchoolSug(Array.isArray(data) ? data : []);
|
||||
} catch (e) {
|
||||
if (!cancelled) setSchoolSug([]);
|
||||
}
|
||||
return tuitionType === 'inState'
|
||||
? parseFloat(match.TUITION5 || match.TUITION6 || '')
|
||||
: parseFloat(match.TUITION7 || '');
|
||||
};
|
||||
})();
|
||||
return () => { cancelled = true; };
|
||||
}, [open, schoolSearch]);
|
||||
|
||||
const est = calc();
|
||||
if (est && !tuitionManual) setTuition(String(est));
|
||||
}, [schoolSearch, tuitionType, degree, cipData, icData, tuitionManual]);
|
||||
|
||||
/* ───────── PROGRAM SUGGEST (server; depends on school) ───────── */
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
if (!selectedSchool?.name) { setProgramSug([]); return; }
|
||||
const q = programSearch.trim();
|
||||
if (q.length < 2) { setProgramSug([]); return; }
|
||||
let cancelled = false;
|
||||
(async () => {
|
||||
try {
|
||||
const { data } = await api.get('/api/programs/suggest', {
|
||||
params: { school: selectedSchool.name, query: q, limit: 10 },
|
||||
withCredentials: true,
|
||||
});
|
||||
if (!cancelled) setProgramSug(Array.isArray(data) ? data : []);
|
||||
} catch (e) {
|
||||
if (!cancelled) setProgramSug([]);
|
||||
}
|
||||
})();
|
||||
return () => { cancelled = true; };
|
||||
}, [open, selectedSchool?.name, programSearch]);
|
||||
|
||||
/* ───────── DEGREE TYPES (server; depends on school+program) ───────── */
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
if (!selectedSchool?.name || !selectedProgram) { setDegreeTypes([]); return; }
|
||||
let cancelled = false;
|
||||
(async () => {
|
||||
try {
|
||||
const { data } = await api.get('/api/programs/types', {
|
||||
params: { school: selectedSchool.name, program: selectedProgram },
|
||||
withCredentials: true,
|
||||
});
|
||||
const types = Array.isArray(data?.types) ? data.types : [];
|
||||
if (!cancelled) setDegreeTypes(types);
|
||||
} catch (e) {
|
||||
if (!cancelled) setDegreeTypes([]);
|
||||
}
|
||||
})();
|
||||
return () => { cancelled = true; };
|
||||
}, [open, selectedSchool?.name, selectedProgram]);
|
||||
|
||||
|
||||
/* ───────── TUITION ESTIMATE (server) ───────── */
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
if (!selectedSchool?.unitId) return;
|
||||
if (!degree) return;
|
||||
if (tuitionManual) return; // user overrode
|
||||
(async () => {
|
||||
try {
|
||||
const { data } = await api.get('/api/tuition/estimate', {
|
||||
params: {
|
||||
unitId : String(selectedSchool.unitId),
|
||||
programType: degree,
|
||||
inState : tuitionType === 'inState' ? 1 : 0,
|
||||
inDistrict: 0,
|
||||
creditHoursPerYear: 0, // use school's full-time figure (server picks full when 0)
|
||||
},
|
||||
withCredentials: true,
|
||||
});
|
||||
if (data?.estimate) setTuition(String(data.estimate));
|
||||
} catch (e) {
|
||||
// keep existing value; user can enter manually
|
||||
}
|
||||
})();
|
||||
},[open, selectedSchool?.unitId, degree, tuitionType, tuitionManual]);
|
||||
|
||||
/* ════════════════════════════════════════════════════
|
||||
ESC --> close convenience
|
||||
@ -200,7 +222,7 @@ const handleContinue = () => {
|
||||
}
|
||||
|
||||
const stub = {
|
||||
name : schoolSearch || 'Unknown School',
|
||||
name : (selectedSchool?.name || schoolSearch || 'Unknown School'),
|
||||
degreeType : degree || 'Unspecified',
|
||||
programLength : 4,
|
||||
tuition : parseFloat(tuition),
|
||||
@ -247,23 +269,89 @@ const handleContinue = () => {
|
||||
className="space-y-4"
|
||||
onSubmit={e => { e.preventDefault(); handleContinue(); }}
|
||||
>
|
||||
{/* School name (optional) */}
|
||||
<div>
|
||||
{/* School name (server suggest) */}
|
||||
<div className="relative">
|
||||
<label className="text-sm font-medium">School name</label>
|
||||
<input
|
||||
type="text"
|
||||
value={schoolSearch}
|
||||
onChange={e => setSchoolSearch(e.target.value)}
|
||||
list="school-suggestions"
|
||||
onChange={e => {
|
||||
setSchoolSearch(e.target.value);
|
||||
setSelectedSchool({ name:'', unitId:null });
|
||||
setSchoolSugOpen(true); setSchoolHi(-1);
|
||||
// Clear program/degree when school changes
|
||||
setProgramSearch(''); setSelectedProgram(''); setDegree(''); setDegreeTypes([]);
|
||||
}}
|
||||
onFocus={() => setSchoolSugOpen(true)}
|
||||
onBlur={() => setTimeout(() => setSchoolSugOpen(false), 120)}
|
||||
placeholder="Start typing…"
|
||||
className="mt-1 w-full rounded border px-3 py-2 text-sm"
|
||||
onBlur={e => setSchoolSearch(e.target.value.trim())}
|
||||
onKeyDown={e => {
|
||||
if (!schoolSugOpen || schoolSug.length === 0) return;
|
||||
if (e.key === 'ArrowDown') { e.preventDefault(); setSchoolHi(h => Math.min(h + 1, schoolSug.length - 1)); }
|
||||
else if (e.key === 'ArrowUp') { e.preventDefault(); setSchoolHi(h => Math.max(h - 1, 0)); }
|
||||
else if (e.key === 'Enter' && schoolHi >= 0) {
|
||||
e.preventDefault();
|
||||
const opt = schoolSug[schoolHi]; setSchoolSearch(opt.name); setSelectedSchool(opt); setSchoolSugOpen(false);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<datalist id="school-suggestions">
|
||||
{suggestions.map((s, i) => (
|
||||
<option key={i} value={s} />
|
||||
{schoolSugOpen && schoolSug.length > 0 && (
|
||||
<div className="absolute z-50 mt-1 w-full max-h-56 overflow-auto rounded border bg-white shadow">
|
||||
{schoolSug.map((s, i) => (
|
||||
<button
|
||||
key={`${s.name}-${s.unitId}-${i}`}
|
||||
type="button"
|
||||
className={`block w-full text-left px-3 py-2 text-sm hover:bg-gray-50 ${schoolHi===i ? 'bg-gray-100' : ''}`}
|
||||
onMouseEnter={() => setSchoolHi(i)}
|
||||
onMouseDown={e => e.preventDefault()}
|
||||
onClick={() => { setSchoolSearch(s.name); setSelectedSchool(s); setSchoolSugOpen(false); }}
|
||||
>
|
||||
{s.name}
|
||||
</button>
|
||||
))}
|
||||
</datalist>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Program (server suggest, requires selected school) */}
|
||||
<div className="relative">
|
||||
<label className="text-sm font-medium">Program / major</label>
|
||||
<input
|
||||
type="text"
|
||||
value={programSearch}
|
||||
onChange={e => { setProgramSearch(e.target.value); setSelectedProgram(''); setProgramSugOpen(true); setProgramHi(-1); }}
|
||||
onFocus={() => setProgramSugOpen(true)}
|
||||
onBlur={() => setTimeout(() => setProgramSugOpen(false), 120)}
|
||||
placeholder={selectedSchool?.name ? 'Start typing…' : 'Pick a school first'}
|
||||
disabled={!selectedSchool?.name}
|
||||
className="mt-1 w-full rounded border px-3 py-2 text-sm disabled:bg-gray-100"
|
||||
onKeyDown={e => {
|
||||
if (!programSugOpen || programSug.length === 0) return;
|
||||
if (e.key === 'ArrowDown') { e.preventDefault(); setProgramHi(h => Math.min(h + 1, programSug.length - 1)); }
|
||||
else if (e.key === 'ArrowUp') { e.preventDefault(); setProgramHi(h => Math.max(h - 1, 0)); }
|
||||
else if (e.key === 'Enter' && programHi >= 0) {
|
||||
e.preventDefault();
|
||||
const opt = programSug[programHi]; setProgramSearch(opt.program); setSelectedProgram(opt.program); setProgramSugOpen(false);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{programSugOpen && programSug.length > 0 && (
|
||||
<div className="absolute z-50 mt-1 w-full max-h-56 overflow-auto rounded border bg-white shadow">
|
||||
{programSug.map((s, i) => (
|
||||
<button
|
||||
key={`${s.program}-${i}`}
|
||||
type="button"
|
||||
className={`block w-full text-left px-3 py-2 text-sm hover:bg-gray-50 ${programHi===i ? 'bg-gray-100' : ''}`}
|
||||
onMouseEnter={() => setProgramHi(i)}
|
||||
onMouseDown={e => e.preventDefault()}
|
||||
onClick={() => { setProgramSearch(s.program); setSelectedProgram(s.program); setProgramSugOpen(false); }}
|
||||
>
|
||||
{s.program}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Residency */}
|
||||
@ -286,6 +374,7 @@ const handleContinue = () => {
|
||||
value={degree}
|
||||
onChange={e => setDegree(e.target.value)}
|
||||
className="mt-1 w-full rounded border px-3 py-2 text-sm"
|
||||
disabled={degreeTypes.length === 0 && !selectedProgram}
|
||||
>
|
||||
<option value="">Select…</option>
|
||||
{degreeMenu.map((d, i) => (
|
||||
|
@ -1,3 +1,4 @@
|
||||
// src/components/PlanningLanding.jsx
|
||||
import React from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Button } from './ui/button.js';
|
||||
@ -6,46 +7,52 @@ function PlanningLanding() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-gray-50 p-6">
|
||||
<div className="max-w-2xl w-full bg-white shadow-lg rounded-lg p-8">
|
||||
<h1 className="text-3xl font-bold mb-4 text-center">
|
||||
<div className="min-h-screen flex items-center justify-center bg-gradient-to-b from-aptiva-gray to-white p-6">
|
||||
<div className="max-w-2xl w-full bg-white shadow-xl rounded-xl p-8 md:p-10">
|
||||
{/* Header */}
|
||||
<div className="text-center mb-8">
|
||||
<h1 className="text-3xl md:text-4xl font-bold text-aptiva mb-2">
|
||||
Planning Your Career
|
||||
</h1>
|
||||
<p className="text-gray-600 mb-6 text-center">
|
||||
Discover career options that match your interests, skills, and potential.
|
||||
AptivaAI helps you find your ideal career path, provides insights into educational requirements,
|
||||
expected salaries, job market trends, and more.
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-1 gap-6">
|
||||
|
||||
<div>
|
||||
<Button className="w-full" onClick={() => navigate('/interest-inventory')}>
|
||||
Take Interest Inventory
|
||||
</Button>
|
||||
<p className="mt-2 text-sm text-gray-500">
|
||||
Identify your interests and discover careers aligned with your strengths.
|
||||
<p className="text-gray-600 max-w-xl mx-auto text-sm md:text-base leading-relaxed">
|
||||
Discover career options that match your interests, skills, and
|
||||
potential. AptivaAI helps you explore ideal paths, understand
|
||||
educational requirements, compare salaries, and analyze job market
|
||||
trends—all in one place.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Button className="w-full" onClick={() => navigate('/career-explorer')}>
|
||||
Explore Career Paths
|
||||
</Button>
|
||||
<p className="mt-2 text-sm text-gray-500">
|
||||
Research detailed career profiles, job descriptions, salaries, and employment outlooks.
|
||||
{/* Options grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{/* Interest Inventory */}
|
||||
<div className="flex flex-col border rounded-lg p-5 hover:shadow-md transition-colors">
|
||||
<h2 className="text-lg font-semibold mb-2">Interest Inventory</h2>
|
||||
<p className="text-sm text-gray-600 mb-4 flex-1">
|
||||
Identify your interests and discover careers aligned with your
|
||||
strengths.
|
||||
</p>
|
||||
<Button
|
||||
className="w-full mt-auto bg-aptiva hover:bg-aptiva-dark text-white"
|
||||
onClick={() => navigate('/interest-inventory')}
|
||||
>
|
||||
Take Inventory
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Button className="w-full" onClick={() => navigate('/educational-programs')}>
|
||||
Discover Educational Programs
|
||||
</Button>
|
||||
<p className="mt-2 text-sm text-gray-500">
|
||||
Find the right educational programs, degrees, and certifications needed to pursue your chosen career.
|
||||
{/* Career Explorer */}
|
||||
<div className="flex flex-col border rounded-lg p-5 hover:shadow-md transition-colors">
|
||||
<h2 className="text-lg font-semibold mb-2">Career Explorer</h2>
|
||||
<p className="text-sm text-gray-600 mb-4 flex-1">
|
||||
Research career profiles, job descriptions, salaries, and
|
||||
employment outlooks.
|
||||
</p>
|
||||
<Button
|
||||
className="w-full mt-auto bg-aptiva hover:bg-aptiva-dark text-white"
|
||||
onClick={() => navigate('/career-explorer')}
|
||||
>
|
||||
Explore Careers
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,13 +1,13 @@
|
||||
// src/components/PreparingLanding.js
|
||||
import React, { useState, useCallback, useEffect, useContext } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useNavigate, useLocation } from 'react-router-dom';
|
||||
import { Button } from './ui/button.js';
|
||||
import LoanRepaymentDrawer from './LoanRepaymentDrawer.js';
|
||||
import { ProfileCtx } from '../App.js';
|
||||
|
||||
function PreparingLanding() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const location = useLocation();
|
||||
const { user } = useContext(ProfileCtx);
|
||||
|
||||
/* ─── Drawer visibility ─────────────────────────────── */
|
||||
@ -19,9 +19,8 @@ function PreparingLanding() {
|
||||
const [userZip] = useState('');
|
||||
const [loanResults, setLoanResults] = useState([]);
|
||||
|
||||
|
||||
/* Esc -to-close convenience */
|
||||
const escHandler = useCallback(e => {
|
||||
const escHandler = useCallback((e) => {
|
||||
if (e.key === 'Escape') setShowLoan(false);
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
@ -29,58 +28,83 @@ function PreparingLanding() {
|
||||
return () => window.removeEventListener('keydown', escHandler);
|
||||
}, [showLoan, escHandler]);
|
||||
|
||||
/* ─── Auto-open drawer when routed from nav ───────── */
|
||||
useEffect(() => {
|
||||
// support either query ?loan=1 or navigation state { openLoan: true }
|
||||
try {
|
||||
const qs = new URLSearchParams(location.search);
|
||||
const viaQuery = qs.get('loan') === '1';
|
||||
const viaState = location.state && location.state.openLoan === true;
|
||||
if ((viaQuery || viaState) && !showLoan) {
|
||||
setShowLoan(true);
|
||||
}
|
||||
} catch {}
|
||||
}, [location.search, location.state, showLoan]);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-gray-50 p-6">
|
||||
<div className="max-w-3xl w-full bg-white shadow-lg rounded-lg p-8 space-y-6">
|
||||
<div className="min-h-screen flex items-center justify-center bg-gradient-to-b from-aptiva-gray to-white p-6">
|
||||
<div className="w-full max-w-3xl bg-white shadow-xl rounded-xl p-8 md:p-10 space-y-8">
|
||||
{/* ───────────────── TITLE / INTRO ───────────────── */}
|
||||
<h1 className="text-3xl font-bold text-center">
|
||||
<div className="text-center">
|
||||
<h1 className="text-3xl md:text-4xl font-bold text-aptiva mb-2">
|
||||
Preparing for Your (Next) Career
|
||||
</h1>
|
||||
<p className="text-gray-600 text-center">
|
||||
<p className="text-gray-600 max-w-2xl mx-auto text-sm md:text-base leading-relaxed">
|
||||
Build the right skills and plan your education so you can confidently
|
||||
enter—or transition into—your new career.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* ──────────────── 1) PATH CHOICE ──────────────── */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-semibold">Which Path Fits You?</h2>
|
||||
<p className="text-gray-700">
|
||||
We can help you identify whether a
|
||||
<strong>skills-based program</strong> (certifications, bootcamps) or a
|
||||
<strong>formal education route</strong> (two- or four-year college)
|
||||
is the best fit. Whichever path you choose, AptivaAI will help you map next steps—from applying to graduating.
|
||||
|
||||
We can help you identify whether a <strong>skills-based program</strong> (certifications, bootcamps) or a{' '}
|
||||
<strong>formal education route</strong> (two- or four-year college) is the best fit. Whichever path you choose,
|
||||
AptivaAI will help you map next steps—from applying to graduating.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row gap-4">
|
||||
<Button onClick={() => navigate('/educational-programs')}>
|
||||
Plan My Education Path
|
||||
</Button>
|
||||
|
||||
<Button onClick={() => setShowLoan(true)}>
|
||||
Cost of Education & Loan Repayment
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{/* Education path card */}
|
||||
<div className="flex flex-col border rounded-lg p-5 hover:shadow-md transition-colors">
|
||||
<h3 className="text-lg font-semibold mb-2">Plan My Education Path</h3>
|
||||
<p className="text-sm text-gray-600 mb-4 flex-1">
|
||||
Compare degrees, certificates, and training paths for your target role.
|
||||
</p>
|
||||
<Button
|
||||
className="w-full mt-auto bg-aptiva hover:bg-aptiva-dark text-white"
|
||||
onClick={() => navigate('/educational-programs')}
|
||||
>
|
||||
Plan Education
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Loan/Cost card */}
|
||||
<div className="flex flex-col border rounded-lg p-5 hover:shadow-md transition-colors">
|
||||
<h3 className="text-lg font-semibold mb-2">Cost of Education & Loan Repayment</h3>
|
||||
<p className="text-sm text-gray-600 mb-4 flex-1">
|
||||
Estimate total costs and monthly payments with realistic assumptions.
|
||||
</p>
|
||||
<Button
|
||||
className="w-full mt-auto bg-aptiva hover:bg-aptiva-dark text-white"
|
||||
onClick={() => setShowLoan(true)}
|
||||
>
|
||||
Open Loan Tool
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ──────────────── 2) LOAN BLURB ──────────────── */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-semibold">Financing Your Future</h2>
|
||||
<p className="text-gray-700">
|
||||
Already have an idea of where you want to enroll? Compare costs,
|
||||
estimate student-loan repayments, and map out work-study or part-time
|
||||
opportunities. Our integrated <strong>LoanRepayment</strong> tool shows
|
||||
realistic monthly payments so you can make confident choices.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* ──────────────── 3) INTEREST INVENTORY ──────────────── */}
|
||||
{/* ──────────────── 2) STILL EXPLORING ──────────────── */}
|
||||
<section className="space-y-3">
|
||||
<h2 className="text-xl font-semibold">Still Exploring?</h2>
|
||||
<p className="text-gray-700">
|
||||
Want to revisit career possibilities? Retake our Interest Inventory to
|
||||
see other matching paths.
|
||||
Want to revisit career possibilities? Retake our Interest Inventory to see other matching paths.
|
||||
</p>
|
||||
<Button onClick={() => navigate('/interest-inventory')}>
|
||||
<Button
|
||||
className="bg-aptiva hover:bg-aptiva-dark text-white"
|
||||
onClick={() => navigate('/interest-inventory')}
|
||||
>
|
||||
Retake Interest Inventory
|
||||
</Button>
|
||||
</section>
|
||||
@ -90,7 +114,11 @@ function PreparingLanding() {
|
||||
{showLoan && (
|
||||
<LoanRepaymentDrawer
|
||||
open={showLoan}
|
||||
onClose={() => setShowLoan(false)}
|
||||
onClose={() => {
|
||||
setShowLoan(false);
|
||||
// clear nav flag so it doesn’t auto-reopen
|
||||
navigate({ pathname: location.pathname }, { replace: true, state: {} });
|
||||
}}
|
||||
schools={schools}
|
||||
setSchools={setSchools}
|
||||
results={loanResults}
|
||||
|
@ -1,25 +1,39 @@
|
||||
import React from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Button } from './ui/button.js';
|
||||
import React from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Button } from "./ui/button.js";
|
||||
|
||||
function RetirementLanding() {
|
||||
export default function RetirementLanding() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-gray-50 p-6">
|
||||
<div className="max-w-2xl w-full bg-white shadow-lg rounded-lg p-8">
|
||||
<h1 className="text-3xl font-bold mb-4 text-center">
|
||||
<div className="min-h-screen bg-gray-50 py-10 px-4">
|
||||
<div className="max-w-3xl mx-auto bg-white rounded-2xl shadow p-6 md:p-8">
|
||||
{/* Title + intro (centered, blue headline) */}
|
||||
<h1 className="text-3xl font-bold text-blue-600 text-center">
|
||||
Retirement Planning
|
||||
</h1>
|
||||
<p className="text-gray-600 mb-6 text-center">
|
||||
Plan strategically and financially for retirement. AptivaAI provides you with clear financial projections, milestone tracking, and scenario analysis for a secure future.
|
||||
<p className="text-center text-gray-600 mt-2">
|
||||
Plan strategically and financially for retirement. Model scenarios,
|
||||
understand trade-offs, and track the milestones that keep you on course.
|
||||
</p>
|
||||
<div className="grid grid-cols-1 gap-4">
|
||||
<Button onClick={() => navigate('/retirement-planner')}>Compare different retirement scenarios and get AI help with planning</Button>
|
||||
|
||||
{/* Feature tile(s) inside main card */}
|
||||
<div className="grid gap-6 md:gap-8 md:grid-cols-2 mt-6">
|
||||
{/* Retirement Planner */}
|
||||
<div className="rounded-xl border border-gray-200 p-5 md:col-span-2">
|
||||
<h3 className="text-lg font-medium">Retirement Planner</h3>
|
||||
<p className="text-gray-600 mt-1">
|
||||
Compare scenarios, project savings and income, and get AI guidance to
|
||||
fine-tune your plan.
|
||||
</p>
|
||||
<div className="mt-4">
|
||||
<Button size="lg" onClick={() => navigate("/retirement-planner")}>
|
||||
Open Retirement Planner
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default RetirementLanding;
|
||||
|
@ -84,14 +84,17 @@ function SignIn({ setIsAuthenticated, setUser }) {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col items-center justify-center bg-gray-100 p-4">
|
||||
<div className="flex min-h-screen flex-col items-center justify-center bg-gradient-to-b from-aptiva-gray to-white p-4">
|
||||
{showSessionExpiredMsg && (
|
||||
<div className="mb-4 p-2 bg-red-100 border border-red-300 text-red-700 rounded">
|
||||
Your session has expired. Please sign in again.
|
||||
</div>
|
||||
)}
|
||||
<div className="w-full max-w-sm rounded-md bg-white p-6 shadow-md">
|
||||
<h1 className="mb-6 text-center text-2xl font-semibold">Sign In</h1>
|
||||
<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>
|
||||
<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>
|
||||
|
||||
{error && (
|
||||
<p className="mb-4 rounded bg-red-50 p-2 text-sm text-red-600">
|
||||
@ -99,24 +102,24 @@ function SignIn({ setIsAuthenticated, setUser }) {
|
||||
</p>
|
||||
)}
|
||||
|
||||
<form onSubmit={handleSignIn} className="flex flex-col space-y-4">
|
||||
<form onSubmit={handleSignIn} className="flex flex-col space-y-3 md:space-y-4">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Username"
|
||||
ref={usernameRef}
|
||||
className="w-full rounded border border-gray-300 p-2 focus:border-blue-500 focus:outline-none"
|
||||
className="w-full rounded border border-gray-300 p-3 md:p-2 h-12 md:h-10 text-base md:text-sm focus:border-aptiva focus:outline-none"
|
||||
autoComplete="username"
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
ref={passwordRef}
|
||||
className="w-full rounded border border-gray-300 p-2 focus:border-blue-500 focus:outline-none"
|
||||
className="w-full rounded border border-gray-300 p-3 md:p-2 h-12 md:h-10 text-base md:text-sm focus:border-aptiva focus:outline-none"
|
||||
autoComplete="current-password"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
className="mx-auto rounded bg-blue-600 px-6 py-2 text-center text-white transition-colors hover:bg-blue-700 focus:outline-none"
|
||||
className="w-full md:w-auto md:mx-auto rounded bg-aptiva px-6 h-12 md:h-10 text-center text-white transition-colors hover:bg-aptiva-dark focus:outline-none"
|
||||
>
|
||||
Sign In
|
||||
</button>
|
||||
@ -125,7 +128,7 @@ function SignIn({ setIsAuthenticated, setUser }) {
|
||||
<div className="mt-4 space-y-2">
|
||||
<p className="text-center text-sm text-gray-600">
|
||||
Don’t have an account?{' '}
|
||||
<Link to="/signup" className="text-blue-600 hover:underline">
|
||||
<Link to="/signup" className="text-aptiva hover:underline">
|
||||
Sign Up
|
||||
</Link>
|
||||
</p>
|
||||
@ -133,7 +136,7 @@ function SignIn({ setIsAuthenticated, setUser }) {
|
||||
<div className="text-center">
|
||||
<Link
|
||||
to="/forgot-password"
|
||||
className="inline-block text-sm text-blue-600 hover:underline"
|
||||
className="inline-block text-sm text-aptiva hover:underline"
|
||||
>
|
||||
Forgot your password?
|
||||
</Link>
|
||||
|
@ -1,44 +1,89 @@
|
||||
// SignInLanding.jsx
|
||||
// src/components/SignInLanding.jsx
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
function SignInLanding({ user }) {
|
||||
return (
|
||||
<div className="max-w-2xl mx-auto p-4 bg-white shadow rounded">
|
||||
<h2 className="text-2xl font-semibold mb-4">
|
||||
Welcome to AptivaAI {user?.firstname}!
|
||||
<div className="max-w-2xl md:max-w-3xl mx-auto p-4 bg-white shadow rounded">
|
||||
{/* Soft brand header band */}
|
||||
<div className="rounded-md bg-aptiva-gray/70 px-4 py-3 mb-4">
|
||||
<div className="text-aptiva text-sm font-semibold tracking-tight">
|
||||
AptivaAI
|
||||
</div>
|
||||
<h2 className="text-xl md:text-2xl font-semibold">
|
||||
Welcome{user?.firstname ? `, ${user.firstname}` : ''}!
|
||||
</h2>
|
||||
<p className="mb-4">
|
||||
At AptivaAI, we aim to arm you with as much information as possible to make informed career decisions. Today’s workplace is changing faster than ever, driven largely by AI—but our goal is to use that same technology to empower job seekers, not replace them.
|
||||
|
||||
We blend data-backed insights with human-centered design, enhanced -not driven by- AI. Giving you practical recommendations and real-world context so you stay in the driver’s seat of your career. Whether you’re planning your first step, advancing your current role, or ready to pivot entirely, our platform keeps you in control—helping you adapt, grow, and thrive on your own terms.
|
||||
<p className="text-xs md:text-sm text-gray-600">
|
||||
Career guidance powered by data — enhanced by AI
|
||||
</p>
|
||||
<ul className="list-disc ml-6 mb-4">
|
||||
</div>
|
||||
|
||||
<li><strong>Planning:</strong> Just starting out? Looking for a different career that is a better fit? Explore options and figure out what careers match your interests and skills.</li>
|
||||
<li><strong>Preparing:</strong> Know what you want but just not how to get there? Gain education, skills, or certifications required to start or transition.</li>
|
||||
<li><strong>Enhancing:</strong> You've got some experience in your field but want to know how to get to the next level? Advance, seek promotions, or shift roles for an established professional.</li>
|
||||
<li><strong>Retirement:</strong> On your happy path and want to make sure you're financially ready when the time comes? Prepare financially and strategically for retirement.</li>
|
||||
</ul>
|
||||
<p className="mb-4">
|
||||
Where would you like to go next?
|
||||
{/* Intro copy */}
|
||||
<p className="mb-4 text-sm md:text-base leading-relaxed md:leading-7">
|
||||
At AptivaAI, we aim to arm you with as much information as possible to
|
||||
make informed career decisions. Today’s workplace is changing faster
|
||||
than ever, driven largely by AI—but our goal is to use that same
|
||||
technology to empower job seekers, not replace them.
|
||||
<br />
|
||||
<br />
|
||||
We blend data-backed insights with human-centered design, enhanced—not
|
||||
driven by—AI. Giving you practical recommendations and real-world
|
||||
context so you stay in the driver’s seat of your career. Whether you’re
|
||||
planning your first step, advancing your current role, or ready to pivot
|
||||
entirely, our platform keeps you in control—helping you adapt, grow, and
|
||||
thrive on your own terms.
|
||||
</p>
|
||||
|
||||
<p className="mb-2 md:mb-3">Where would you like to go next?</p>
|
||||
|
||||
{/* Four clickable tiles */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3 md:gap-4">
|
||||
<Link
|
||||
to="/planning"
|
||||
className="block rounded-lg border px-4 py-3 transition-colors bg-white hover:bg-aptiva hover:text-white focus:outline-none focus:ring-2 focus:ring-aptiva"
|
||||
>
|
||||
<div className="text-sm font-semibold">Planning</div>
|
||||
<p className="text-sm">
|
||||
Just starting out or exploring a better fit? See careers that match
|
||||
your interests and skills.
|
||||
</p>
|
||||
{/* Mobile: stacked full-width; Desktop: original inline buttons with spacing */}
|
||||
<div className="flex flex-col gap-2 md:block md:space-x-2">
|
||||
<Link to="/planning" className="w-full md:w-auto h-12 md:h-auto inline-flex items-center justify-center md:inline-block px-4 bg-blue-600 text-white rounded hover:bg-blue-700">
|
||||
Go to Exploring
|
||||
</Link>
|
||||
<Link to="/preparing" className="w-full md:w-auto h-12 md:h-auto inline-flex items-center justify-center md:inline-block px-4 bg-blue-600 text-white rounded hover:bg-blue-700">
|
||||
Go to Preparing
|
||||
|
||||
<Link
|
||||
to="/preparing"
|
||||
className="block rounded-lg border px-4 py-3 transition-colors bg-white hover:bg-aptiva hover:text-white focus:outline-none focus:ring-2 focus:ring-aptiva"
|
||||
>
|
||||
<div className="text-sm font-semibold">Preparing</div>
|
||||
<p className="text-sm">
|
||||
Know your target role but not the path? Find education, skills, or
|
||||
certifications to start or transition.
|
||||
</p>
|
||||
</Link>
|
||||
<Link to="/enhancing" className="w-full md:w-auto h-12 md:h-auto inline-flex items-center justify-center md:inline-block px-4 bg-green-600 text-white rounded hover:bg-green-700">
|
||||
Go to Enhancing
|
||||
|
||||
<Link
|
||||
to="/enhancing"
|
||||
className="block rounded-lg border px-4 py-3 transition-colors bg-white hover:bg-aptiva hover:text-white focus:outline-none focus:ring-2 focus:ring-aptiva"
|
||||
>
|
||||
<div className="text-sm font-semibold">Enhancing</div>
|
||||
<p className="text-sm">
|
||||
Already in-field? Chart your next step—promotions, role shifts, and
|
||||
growth moves.
|
||||
</p>
|
||||
</Link>
|
||||
<Link to="/retirement" className="w-full md:w-auto h-12 md:h-auto inline-flex items-center justify-center md:inline-block px-4 bg-green-600 text-white rounded hover:bg-green-700">
|
||||
Go to Retirement
|
||||
|
||||
<Link
|
||||
to="/retirement"
|
||||
className="block rounded-lg border px-4 py-3 transition-colors bg-white hover:bg-aptiva hover:text-white focus:outline-none focus:ring-2 focus:ring-aptiva"
|
||||
>
|
||||
<div className="text-sm font-semibold">Retirement</div>
|
||||
<p className="text-sm">
|
||||
On your happy path? Make sure the finances line up when the time
|
||||
comes.
|
||||
</p>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user