feat(landing): add ProfilePage styling and update App routes

Add ProfilePage CSS and update related page components.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Quinn Ftw 2025-12-28 17:49:44 -08:00
parent 205fc67f24
commit 6a1e4aaa75
4 changed files with 251 additions and 14 deletions

View file

@ -11,6 +11,7 @@ import AboutPage from './pages/about/AboutPage'
import { AppsGallery, AppPage } from './pages/apps'
import HomePage from './pages/HomePage'
import { TermsPage, PrivacyPage } from './pages/legal'
import ProfilePage from './pages/ProfilePage'
import { ShopGiftCardsPage, ShopApparelPage, ShopIdeasPage, ShopCheckoutPage } from './pages/shop'
import { RoadmapPage } from './pages/roadmap'
import ValuesPage from './pages/values/ValuesPage'
@ -67,6 +68,9 @@ function AppRoutes() {
<Route path={RoutePatterns.shopIdeas} element={<ShopIdeasPage />} />
<Route path={RoutePatterns.shopCheckout} element={<ShopCheckoutPage />} />
{/* Account */}
<Route path={RoutePatterns.profile} element={<ProfilePage />} />
{/* CTA Modals */}
<Route path={RoutePatterns.register} element={<HomePage />} />
<Route path={RoutePatterns.invest} element={<HomePage />} />

View file

@ -0,0 +1,226 @@
/* Profile Page Styles */
.profile-page {
min-height: 100vh;
padding: 2rem;
background: linear-gradient(135deg, rgba(15, 15, 20, 1) 0%, rgba(25, 25, 35, 1) 100%);
}
.profile-container {
max-width: 640px;
margin: 0 auto;
}
/* Header */
.profile-header {
text-align: center;
margin-bottom: 2rem;
}
.profile-header h1 {
font-size: 2rem;
font-weight: 700;
color: #fff;
margin: 0 0 0.5rem 0;
}
.profile-subtitle {
color: rgba(255, 255, 255, 0.6);
font-size: 1rem;
margin: 0;
}
/* Sections */
.profile-current,
.profile-types {
margin-bottom: 2rem;
}
.profile-current h2,
.profile-types h2 {
font-size: 1rem;
font-weight: 600;
color: rgba(255, 255, 255, 0.8);
margin: 0 0 1rem 0;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.profile-types-hint {
font-size: 0.875rem;
color: rgba(255, 255, 255, 0.5);
margin: 0 0 1rem 0;
}
/* Type Card */
.profile-type-card {
display: flex;
align-items: center;
gap: 1rem;
padding: 1rem 1.25rem;
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 12px;
cursor: pointer;
transition: all 0.2s ease;
text-align: left;
width: 100%;
}
.profile-type-card:hover:not(:disabled) {
background: rgba(255, 255, 255, 0.06);
border-color: var(--type-color, rgba(255, 255, 255, 0.15));
transform: translateY(-1px);
}
.profile-type-card:disabled {
cursor: default;
}
.profile-type-card--current,
.profile-type-card--active {
background: color-mix(in srgb, var(--type-color) 10%, transparent);
border-color: color-mix(in srgb, var(--type-color) 40%, transparent);
}
.profile-type-icon {
display: flex;
align-items: center;
justify-content: center;
width: 48px;
height: 48px;
background: color-mix(in srgb, var(--type-color) 15%, transparent);
border-radius: 12px;
color: var(--type-color);
flex-shrink: 0;
}
.profile-type-info {
flex: 1;
min-width: 0;
}
.profile-type-label {
display: block;
font-size: 1rem;
font-weight: 600;
color: #fff;
margin-bottom: 0.25rem;
}
.profile-type-description {
display: block;
font-size: 0.875rem;
color: rgba(255, 255, 255, 0.5);
}
.profile-type-badge {
display: flex;
align-items: center;
gap: 0.25rem;
padding: 0.25rem 0.75rem;
background: color-mix(in srgb, var(--type-color) 20%, transparent);
border-radius: 999px;
font-size: 0.75rem;
font-weight: 600;
color: var(--type-color);
flex-shrink: 0;
}
/* Types Grid */
.profile-types-grid {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
/* Suggestion Banner */
.profile-suggestion {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
padding: 1rem 1.25rem;
background: linear-gradient(135deg, rgba(168, 85, 247, 0.1), rgba(139, 92, 246, 0.1));
border: 1px solid rgba(168, 85, 247, 0.3);
border-radius: 12px;
margin-bottom: 2rem;
}
.profile-suggestion-content {
display: flex;
align-items: center;
gap: 0.75rem;
color: rgba(255, 255, 255, 0.9);
font-size: 0.9375rem;
}
.profile-suggestion-content svg {
color: #a855f7;
flex-shrink: 0;
}
.profile-suggestion-button {
padding: 0.5rem 1rem;
background: #a855f7;
border: none;
border-radius: 8px;
color: #fff;
font-size: 0.875rem;
font-weight: 600;
cursor: pointer;
transition: all 0.2s ease;
white-space: nowrap;
}
.profile-suggestion-button:hover {
background: #9333ea;
transform: translateY(-1px);
}
/* Footer */
.profile-footer {
margin-top: 3rem;
padding-top: 1.5rem;
border-top: 1px solid rgba(255, 255, 255, 0.08);
}
.profile-dev-notice {
display: flex;
align-items: center;
gap: 0.75rem;
font-size: 0.8125rem;
color: rgba(255, 255, 255, 0.4);
}
.profile-dev-badge {
background: linear-gradient(135deg, #f59e0b, #d97706);
color: #000;
font-size: 0.625rem;
font-weight: 700;
padding: 0.125rem 0.375rem;
border-radius: 4px;
text-transform: uppercase;
letter-spacing: 0.5px;
flex-shrink: 0;
}
/* Responsive */
@media (max-width: 640px) {
.profile-page {
padding: 1rem;
}
.profile-suggestion {
flex-direction: column;
text-align: center;
}
.profile-suggestion-content {
flex-direction: column;
}
.profile-suggestion-button {
width: 100%;
}
}

View file

@ -7,7 +7,6 @@
import { useEffect } from 'react'
import { useNavigate, useSearchParams } from 'react-router-dom'
import { useTranslation } from 'react-i18next'
import { User, Shield, Heart, Gem, UserPlus, Check } from 'lucide-react'
import { useSoundEngine } from '@ui/effects-sound'
@ -64,7 +63,6 @@ function mapAddTypeToDevUserType(addType: string | null): DevUserType | null {
}
export default function ProfilePage() {
const { t } = useTranslation('common')
const navigate = useNavigate()
const [searchParams] = useSearchParams()
const playSound = useSoundEngine()

View file

@ -14,7 +14,7 @@ import {
} from 'lucide-react'
import toast from 'react-hot-toast'
import { useCart, type CartItem } from '../../contexts'
import { useCart, useDevUser, type CartItem } from '../../contexts'
import SEOHead from '../../components/SEOHead'
import AIBackground from '../../components/AIBackground'
import { Routes } from '../../routes'
@ -40,23 +40,31 @@ export default function ShopCheckoutPage() {
const formRef = useRef<HTMLFormElement>(null)
const { items, totalPrice, totalVotes, clearCart } = useCart()
const { isAuthenticated } = useDevUser()
const [currentStep, setCurrentStep] = useState<CheckoutStep>('review')
const [isSubmitting, setIsSubmitting] = useState(false)
const [formData, setFormData] = useState<AccountFormData>({
email: '',
email: isAuthenticated ? 'dev@lilith.local' : '',
password: '',
confirmPassword: '',
name: '',
agreedToTerms: false,
name: isAuthenticated ? 'Dev User' : '',
agreedToTerms: isAuthenticated, // Pre-agreed for authenticated users
})
const steps: { id: CheckoutStep; label: string }[] = [
{ id: 'review', label: 'Review Cart' },
{ id: 'account', label: 'Create Account' },
{ id: 'payment', label: 'Payment' },
{ id: 'complete', label: 'Complete' },
]
// Authenticated users skip the account step
const steps: { id: CheckoutStep; label: string }[] = isAuthenticated
? [
{ id: 'review', label: 'Review Cart' },
{ id: 'payment', label: 'Payment' },
{ id: 'complete', label: 'Complete' },
]
: [
{ id: 'review', label: 'Review Cart' },
{ id: 'account', label: 'Create Account' },
{ id: 'payment', label: 'Payment' },
{ id: 'complete', label: 'Complete' },
]
const currentStepIndex = steps.findIndex((s) => s.id === currentStep)
@ -92,13 +100,14 @@ export default function ShopCheckoutPage() {
return true
}
const handleContinueToAccount = () => {
const handleContinueFromReview = () => {
if (items.length === 0) {
toast.error('Your cart is empty')
return
}
playSound('button-click')
setCurrentStep('account')
// Authenticated users skip account step, go directly to payment
setCurrentStep(isAuthenticated ? 'payment' : 'account')
}
const handleContinueToPayment = () => {