Header UI

This commit is contained in:
Josh 2025-05-22 17:18:57 +00:00
parent 9d5471b55e
commit 1e9af5a13d
9 changed files with 243 additions and 576 deletions

View File

@ -1,8 +1,8 @@
module.exports = {
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
}

View File

@ -8,6 +8,7 @@ import {
Link,
} from 'react-router-dom';
import { Button } from './components/ui/button.js';
import { cn } from './utils/cn.js';
// Import all components
import PremiumRoute from './components/PremiumRoute.js';
@ -41,18 +42,20 @@ function App() {
const [user, setUser] = useState(null);
const [isLoading, setIsLoading] = useState(true);
// If you want Resume Optimizer to be considered a "premium path" too:
// Define premium paths (including /enhancing and /retirement)
const premiumPaths = [
'/milestone-tracker',
'/paywall',
'/financial-profile',
'/multi-scenario',
'/premium-onboarding',
'/resume-optimizer', // add it here if you want
'/enhancing', // corrected spelling
'/retirement',
'/resume-optimizer',
];
const showPremiumCTA = !premiumPaths.includes(location.pathname);
// 2) We'll define "canAccessPremium" to handle *both* is_premium or is_pro_premium
// We'll define "canAccessPremium" for user
const canAccessPremium = user?.is_premium || user?.is_pro_premium;
// Rehydrate user if there's a token
@ -63,7 +66,7 @@ function App() {
return;
}
// Validate token/fetch user
// Validate token/fetch user profile
fetch('https://dev1.aptivaai.com/api/user-profile', {
headers: { Authorization: `Bearer ${token}` },
})
@ -106,118 +109,184 @@ function App() {
return (
<div className="flex min-h-screen flex-col bg-gray-50 text-gray-800">
{/* Header */}
<header className="flex items-center justify-between border-b bg-white px-6 py-4 shadow-sm">
<div className="flex items-center space-x-8">
<h1 className="text-lg font-semibold">
AptivaAI - Career Guidance Platform (beta)
</h1>
<header className="flex items-center justify-between border-b bg-white px-6 py-4 shadow-sm relative">
<h1 className="text-lg font-semibold">
AptivaAI - Career Guidance Platform (beta)
</h1>
{isAuthenticated && (
<nav>
<ul className="flex space-x-4">
{/* Free sections */}
<li>
<Link className="text-blue-600 hover:text-blue-800" to="/getting-started">
Getting Started
</Link>
</li>
<li>
<Link className="text-blue-600 hover:text-blue-800" to="/career-explorer">
{isAuthenticated && (
<>
{/* NAV MENU */}
<nav className="flex space-x-6">
{/* 1) Find Your Career */}
<div className="relative group">
<Button
style={{ color: '#1f2937' }}
className={`
bg-white
border border-gray-300
hover:bg-gray-100
hover:text-blue-700
whitespace-nowrap
text-xs sm:text-sm md:text-base
font-semibold
`}
>
Find Your Career
</Button>
<div className="absolute top-full left-0 hidden group-hover:block bg-white border shadow-md w-48 z-50">
<Link
to="/career-explorer"
className="block px-4 py-2 hover:bg-gray-100 text-sm text-gray-700"
>
Career Explorer
</Link>
</li>
<li>
<Link className="text-blue-600 hover:text-blue-800" to="/educational-programs">
Skills/Educational Planner
</Link>
</li>
<li>
<Link className="text-blue-600 hover:text-blue-800" to="/interest-inventory">
<Link
to="/interest-inventory"
className="block px-4 py-2 hover:bg-gray-100 text-sm text-gray-700"
>
Interest Inventory
</Link>
</li>
<li>
<Link className="text-blue-600 hover:text-blue-800" to="/profile">
Profile
</div>
</div>
{/* 2) Prepare for Your Career */}
<div className="relative group">
<Button
style={{ color: '#1f2937' }}
className={`
bg-white
border border-gray-300
hover:bg-gray-100
hover:text-blue-700
whitespace-nowrap
text-xs sm:text-sm md:text-base
font-semibold
`}
>
Prepare for Your Career
</Button>
<div className="absolute top-full left-0 hidden group-hover:block bg-white border shadow-md w-56 z-50">
<Link
to="/preparing"
className="block px-4 py-2 hover:bg-gray-100 text-sm text-gray-700"
>
Preparing Landing
</Link>
</li>
<Link
to="/educational-programs"
className="block px-4 py-2 hover:bg-gray-100 text-sm text-gray-700"
>
Educational Programs
</Link>
</div>
</div>
{/* Premium sections */}
<li>
{user?.is_premium ? (
<Link className="text-blue-600 hover:text-blue-800" to="/milestone-tracker">
Career Planner
</Link>
) : (
<span className="text-gray-400 cursor-not-allowed">
Career/Financial Planner{' '}
<span className="text-green-600">(Premium)</span>
</span>
{/* 3) Enhancing Your Career (Premium) */}
<div className="relative group">
<Button
style={{ color: '#1f2937' }}
className={`
bg-white
border border-gray-300
hover:bg-gray-100
hover:text-blue-700
whitespace-nowrap
text-xs sm:text-sm md:text-base
font-semibold
`}
>
Enhancing Your Career
{!canAccessPremium && (
<span className="text-xs ml-1 text-gray-600">(Premium)</span>
)}
</li>
<li>
{user?.is_premium ? (
<Link className="text-blue-600 hover:text-blue-800" to="/financial-profile">
Financial Profile
</Link>
) : (
<span className="text-gray-400 cursor-not-allowed">
Financial Profile{' '}
<span className="text-green-600">(Premium)</span>
</span>
</Button>
<div className="absolute top-full left-0 hidden group-hover:block bg-white border shadow-md w-56 z-50">
{/* Add your premium sub-links here */}
</div>
</div>
{/* 4) Retirement Planning (Premium) */}
<div className="relative group">
<Button
style={{ color: '#1f2937' }}
className={`
bg-white
border border-gray-300
hover:bg-gray-100
hover:text-blue-700
whitespace-nowrap
text-xs sm:text-sm md:text-base
font-semibold
`}
>
Retirement Planning
{!canAccessPremium && (
<span className="text-xs ml-1 text-gray-600">(Premium)</span>
)}
</li>
<li>
{user?.is_premium ? (
<Link className="text-blue-600 hover:text-blue-800" to="/multi-scenario">
Multi Scenario
</Link>
) : (
<span className="text-gray-400 cursor-not-allowed">
Compare Career/Financial Scenarios{' '}
<span className="text-green-600">(Premium)</span>
</span>
)}
</li>
<li>
{canAccessPremium ? (
<Link className="text-blue-600 hover:text-blue-800" to="/resume-optimizer">
Resume Optimizer
</Link>
) : (
<span className="text-gray-400 cursor-not-allowed">
Resume Optimizer{' '}
<span className="text-green-600">(Premium)</span>
</span>
)}
</li>
</ul>
</Button>
<div className="absolute top-full left-0 hidden group-hover:block bg-white border shadow-md w-56 z-50">
{/* Add your premium sub-links here */}
</div>
</div>
{/* 5) Profile */}
<div className="relative group">
<Button
style={{ color: '#1f2937' }}
className={`
bg-white
border border-gray-300
hover:bg-gray-100
hover:text-blue-700
whitespace-nowrap
text-xs sm:text-sm md:text-base
font-semibold
min-w-0
max-w-[90px]
truncate
`}
>
Profile
</Button>
<div className="absolute top-full left-0 hidden group-hover:block bg-white border shadow-md w-48 z-50">
{/* Account Profile, Financial Profile links */}
</div>
</div>
</nav>
)}
</div>
{/* Grouped Logout and Upgrade buttons */}
{isAuthenticated && (
<div className="flex items-center space-x-4">
<button
className="text-red-600 hover:text-red-800 bg-transparent border-none"
onClick={handleLogout}
>
Logout
</button>
{showPremiumCTA && !canAccessPremium && (
<Button
className="bg-green-600 hover:bg-green-700 max-w-fit text-center"
onClick={() => navigate('/paywall')}
>
Upgrade to Premium
</Button>
)}
</div>
{/* LOGOUT + UPGRADE BUTTONS */}
<div className="flex items-center space-x-4 ml-4 relative z-10">
{showPremiumCTA && !canAccessPremium && (
<Button
className="
bg-green-500 hover:bg-green-600
max-w-fit text-center text-white
px-3 py-2
rounded
whitespace-nowrap
text-sm
font-semibold
shadow
"
style={{ minWidth: 0, width: 'auto' }}
onClick={() => navigate('/paywall')}
>
Upgrade to Premium
</Button>
)}
<button
className="text-red-600 hover:text-red-800 bg-transparent border-none"
onClick={handleLogout}
>
Logout
</button>
</div>
</>
)}
</header>
{/* Main Content */}
<main className="flex-1 p-6">
<Routes>
@ -245,12 +314,31 @@ function App() {
<Route path="/profile" element={<UserProfile />} />
<Route path="/planning" element={<PlanningLanding />} />
<Route path="/career-explorer" element={<CareerExplorer />} />
<Route path="/educational-programs" element={<EducationalProgramsPage/>}/>
<Route path="/educational-programs" element={<EducationalProgramsPage />} />
<Route path="/preparing" element={<PreparingLanding />} />
<Route path="/enhancing" element={<EnhancingLanding />} />
<Route path="/retirement" element={<RetirementLanding />} />
{/* Premium-only routes */}
{/*
1) EnhancingLanding is premium-only
2) RetirementLanding is premium-only
*/}
<Route
path="/enhancing"
element={
<PremiumRoute user={user}>
<EnhancingLanding />
</PremiumRoute>
}
/>
<Route
path="/retirement"
element={
<PremiumRoute user={user}>
<RetirementLanding />
</PremiumRoute>
}
/>
{/* Other Premium-only routes */}
<Route
path="/milestone-tracker"
element={
@ -305,7 +393,6 @@ function App() {
<SessionExpiredHandler />
</div>
);
}
export default App;

View File

@ -723,7 +723,7 @@ function CareerExplorer() {
<div className="flex justify-between items-center mb-4">
<h2 className="text-xl font-semibold">
Explore Careers - use the tools below to find your perfect career
Explore Careers - use these tools to find your best fit
</h2>
<CareerSearch
onCareerSelected={(careerObj) => {

View File

@ -102,7 +102,7 @@ function EducationalProgramsPage() {
'Youre about to move to the financial planning portion of the app, which is reserved for premium subscribers. Do you want to continue?'
);
if (proceed) {
navigate('/financial-planner', { state: { selectedSchool: school } });
navigate('/milestone-tracker', { state: { selectedSchool: school } });
};
};

View File

@ -1,378 +0,0 @@
.popout-panel {
position: fixed;
top: 0;
right: 0;
width: 60%; /* Increase width for larger screens */
height: 100%;
background-color: #fff;
box-shadow: -3px 0 5px rgba(0, 0, 0, 0.3);
padding: 20px;
overflow-y: auto;
transform: translateX(0); /* Ensure visibility by default */
transition: transform 0.3s ease-in-out;
z-index: 1000; /* Ensures it is above other elements */
box-sizing: border-box; /* Ensures padding is included in width calculation */
}
.popout-panel.hidden {
display: none;
}
.popout-panel.visible {
display: block;
}
.panel-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
/* Mobile responsiveness */
@media (max-width: 768px) {
.popout-panel {
width: 100%; /* Use full width for smaller screens */
height: 100%; /* Cover full height */
left: 0; /* Ensure it appears on the left for mobile */
right: unset; /* Override right alignment */
}
.schools-offering {
grid-template-columns: 1fr; /* Single column layout for smaller screens */
}
}
/* Close button adjustments for mobile */
.close-btn {
background-color: #dc3545;
color: #fff;
border: none;
padding: 8px 12px;
font-size: 1rem;
cursor: pointer;
z-index: 1001; /* Keep button above the panel */
}
.plan-path-btn {
background-color: #4A90E2;
color: white;
border: none;
padding: 10px 15px;
cursor: pointer;
font-size: 16px;
border-radius: 5px;
text-align: center;
}
/* Job Description and Expected Tasks section */
.section {
margin-bottom: 20px;
}
.job-description,
.expected-tasks {
padding: 10px;
border: 1px solid #e0e0e0;
border-radius: 8px;
background-color: #f9f9f9;
}
/* Expected Tasks Styling */
.expected-tasks {
padding: 15px;
background-color: #fafafa;
border: 1px solid #e0e0e0;
border-radius: 8px;
margin-top: 20px;
}
.expected-tasks ul {
list-style-position: inside; /* Move the bullets inside, aligning them with text */
padding-left: 20px; /* Add space between the bullet and the text */
margin: 0;
text-align: left; /* Align the text to the left */
}
.expected-tasks li {
margin-bottom: 15px; /* Space between each task */
padding-bottom: 10px;
border-bottom: 1px solid #ddd;
font-size: 1rem;
}
/* Title and task text styling */
.expected-tasks h3 {
margin-bottom: 15px;
font-size: 1.2rem;
font-weight: bold;
border-bottom: 2px solid #ccc;
padding-bottom: 5px;
}
.expected-tasks p {
font-size: 1rem;
color: #666;
}
/* Specific styles for Popout Panel */
.popout-panel .filter-container {
display: block;
gap: 15px;
justify-content: center;
margin-bottom: 20px;
}
.popout-panel .filter-group {
display: block;
gap: 5px;
padding-right: 20px;
padding-left: 20px;
padding-top: 20px;
padding-bottom: 20px;
box-sizing: border-box;
}
.popout-panel .filter-group label {
font-size: 1rem;
font-weight: 600;
margin-bottom: 2px;
white-space: nowrap;
}
.popout-panel .filter-group input[type="range"] {
width: 100%;
box-sizing: border-box;
}
.popout-panel .range-labels {
display: flex;
font-size: 0.9rem;
}
/* Schools section: Grid layout with clear separation */
.schools-offering {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); /* Adjust the minimum size of each column */
gap: 20px; /* Space between columns */
margin-top: 20px;
width: 100%; /* Ensure it uses full width of its container */
text-align: center;
justify-content: center; /* Centers grid elements horizontally */
}
.no-schools-message {
text-align: center;
width: 100%;
grid-column: 1 / -1; /* Forces the message to span all columns */
justify-self: center; /* Centers text horizontally */
align-self: center; /* Centers text vertically */
font-style: italic; /* Optional: Stylize the message */
padding: 20px 0; /* Adds spacing */
}
.schools-offering .school-card {
border: 1px solid #ddd;
padding: 15px;
background-color: #f9f9f9;
border-radius: 8px;
display: flex;
flex-direction: column;
text-align: left;
}
.schools-offering .school-card div {
margin-bottom: 8px;
}
.school-info {
display: flex;
flex-direction: column;
gap: 10px;
}
/* Salary Data Section */
.salary-data {
margin-top: 20px;
padding: 15px;
background-color: #fafafa;
border: 1px solid #e0e0e0;
border-radius: 8px;
}
.salary-data table {
width: 60%;
border-collapse: collapse;
margin: 0 auto 20px; /* This centers the table */
}
.salary-data th, .salary-data td {
padding: 10px;
text-align: center;
border-bottom: 1px solid #ddd;
}
.salary-data th {
background-color: #f0f0f0;
font-weight: bold;
}
.salary-data td {
font-size: 1rem;
text-align: center;
}
.salary-data td:last-child {
text-align: center;
}
/* Economic Projections Section */
.economic-projections {
margin-top: 20px;
padding: 15px;
background-color: #fafafa;
border: 1px solid #e0e0e0;
border-radius: 8px;
}
.economic-projections ul {
padding-left: 20px;
list-style-position: inside;
font-size: 1rem;
margin: 0;
}
.economic-projections li {
margin-bottom: 10px;
}
/* Loan Repayment Section Styling */
.loan-repayment-container {
padding: 20px;
background-color: #fafafa;
border-radius: 8px;
}
.loan-repayment-fields label {
display: block;
margin-bottom: 10px;
font-weight: bold;
font-size: 1rem;
}
.loan-repayment-fields input,
.loan-repayment-fields select {
height: 40px;
padding: 0 10px;
font-size: 1rem;
width: 100%;
box-sizing: border-box;
margin-bottom: 15px;
border: 1px solid #ccc;
border-radius: 8px;
}
.loan-repayment-fields button {
width: 100%;
padding: 12px;
height: 45px;
margin-top: 10px;
background-color: #4CAF50;
color: white;
border: none;
text-align: center;
font-size: 1.1rem;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
border-radius: 8px;
transition: background-color 0.3s;
}
.loan-repayment-fields button:hover {
background-color: #45a049;
}
.loan-repayment-fields button:disabled {
background-color: #ccc;
cursor: not-allowed;
}
.loan-repayment-fields {
max-width: 600px;
margin: 0 auto;
padding: 20px;
background-color: #fff;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.loan-repayment-fields input,
.loan-repayment-fields select,
.loan-repayment-fields button {
margin-top: 5px;
margin-bottom: 10px;
}
.loan-repayment-fields h3 {
text-align: center;
margin-bottom: 20px;
}
button {
background-color: #4CAF50;
color: white;
border: none;
cursor: pointer;
text-align: right; /* Centers the button text */
}
/* Comparison Section Styling */
.school-comparison-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); /* Dynamic columns */
gap: 20px; /* Space between cards */
margin-top: 20px;
width: 100%; /* Ensure it uses full width of its container */
}
.school-comparison {
display: grid;
margin-bottom: 25px;
padding: 20px;
border: 1px solid #ddd;
border-radius: 8px;
background-color: #fff;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.school-comparison h4 {
font-size: 1.3rem;
font-weight: bold;
margin-bottom: 10px;
}
/* Section Title Styling */
h3 {
font-size: 1.2rem;
font-weight: bold;
border-bottom: 2px solid #ccc;
padding-bottom: 5px;
margin-bottom: 10px;
}
a {
color: #1a73e8;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
/* Fix for overflow issue */
html, body {
overflow-x: hidden; /* Prevent horizontal scrolling */
}

View File

@ -2,7 +2,6 @@ import React, { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { ClipLoader } from "react-spinners";
import LoanRepayment from "./LoanRepayment.js";
import "./PopoutPanel.css"; // You can keep or remove depending on your needs
function PopoutPanel({
isVisible,

View File

@ -9,28 +9,35 @@ const careerSituations = [
id: "planning",
title: "Planning Your Career",
description: "I'm exploring options and figuring out what careers fit my interests and skills.",
route: "/planning"
route: "/planning",
isPremiumFocus: false
},
{
id: "preparing",
title: "Preparing for Your (Next) Career",
description: "I'm gaining education, skills, or certifications required to start or transition into a new career.",
route: "/preparing"
route: "/preparing",
isPremiumFocus: false
},
{
id: "enhancing",
title: "Enhancing Your Career",
description: "I'm established professionally and want to advance, seek promotions, or shift roles.",
route: "/enhancing"
premiumNote: "(Some advanced features require a Premium subscription)",
route: "/enhancing",
isPremiumFocus: true // <-- add this
},
{
id: "retirement",
title: "Retirement Planning",
description: "I'm preparing financially and strategically for retirement.",
route: "/retirement"
premiumNote: "(Some advanced features require a Premium subscription)",
route: "/retirement",
isPremiumFocus: true // <-- add this
}
];
function SignUp() {
const navigate = useNavigate();
@ -337,6 +344,8 @@ return (
setSelectedSituation(situation);
setShowPrompt(true);
}}
isPremiumFocus={situation.isPremiumFocus}
premiumNote={situation.premiumNote} // <--- pass along
/>
))}
</div>

View File

@ -1,68 +0,0 @@
.user-profile-container {
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
max-width: 600px;
margin: 0 auto;
border: 1px solid #ccc;
border-radius: 10px;
background-color: #f9f9f9;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.user-profile-container h2 {
font-size: 24px;
color: #333;
margin-bottom: 20px;
}
.user-profile-form {
width: 100%;
}
.user-profile-form label {
display: block;
font-size: 16px;
font-weight: bold;
color: #555;
margin-bottom: 5px;
}
.user-profile-form input,
.user-profile-form select {
width: 100%;
padding: 10px;
font-size: 14px;
border: 1px solid #ccc;
border-radius: 5px;
margin-bottom: 15px;
box-sizing: border-box;
}
.user-profile-form button {
padding: 10px 20px;
font-size: 16px;
color: #fff;
background-color: #007bff;
border: none;
border-radius: 5px;
cursor: pointer;
}
.user-profile-form button:hover {
background-color: #0056b3;
}
.error-message {
color: #d9534f;
font-size: 14px;
margin-bottom: 15px;
}
.success-message {
color: #5cb85c;
font-size: 14px;
margin-bottom: 15px;
}

View File

@ -1,8 +1,9 @@
// SituationCard.jsx
import React from 'react';
import { Card, CardContent } from './card.js';
import { cn } from '../../utils/cn.js';
const SituationCard = ({ title, description, selected, onClick }) => (
const SituationCard = ({ title, description, selected, onClick, isPremiumFocus, premiumNote }) => (
<Card
className={cn(
'cursor-pointer transition-shadow duration-200 hover:shadow-lg',
@ -11,8 +12,25 @@ const SituationCard = ({ title, description, selected, onClick }) => (
onClick={onClick}
>
<CardContent>
<h3 className="text-lg font-semibold">{title}</h3>
<p className="text-sm text-gray-600 mt-1">{description}</p>
<div className="flex items-start justify-between">
<div>
<h3 className="text-lg font-semibold">{title}</h3>
<p className="text-sm text-gray-600 mt-1">{description}</p>
{/* Show premium note if provided */}
{isPremiumFocus && premiumNote && (
<p className="text-xs text-yellow-600 mt-2 italic">
{premiumNote}
</p>
)}
</div>
{isPremiumFocus && (
<span className="ml-2 inline-flex items-center px-2 py-1 rounded-full bg-yellow-400 text-xs font-bold">
Premium
</span>
)}
</div>
</CardContent>
</Card>
);