feat: frontend redesign and app structure refactor
This commit is contained in:
parent
527bda634b
commit
6d53ac2b72
30 changed files with 891 additions and 370 deletions
20
deploy_ui.sh
Executable file
20
deploy_ui.sh
Executable file
|
|
@ -0,0 +1,20 @@
|
|||
#!/bin/bash
|
||||
# Deploy UI changes to Gemini VPS
|
||||
|
||||
VPS_IP="216.158.230.94"
|
||||
# Assuming root or the user has SSH config for this IP.
|
||||
# If you use a different user, please export VPS_USER="your-user" before running or edit this line.
|
||||
VPS_USER="${VPS_USER:-root}"
|
||||
REMOTE_PATH="/srv/containers/mtad-gemini"
|
||||
|
||||
echo "Deploying to $VPS_USER@$VPS_IP..."
|
||||
|
||||
echo "1. Syncing web directory..."
|
||||
# Sync web folder, excluding heavy/unnecessary files
|
||||
rsync -avz --exclude 'node_modules' --exclude '.next' --exclude '.git' ./web/ $VPS_USER@$VPS_IP:$REMOTE_PATH/web/
|
||||
|
||||
echo "2. Rebuilding frontend container..."
|
||||
# Rebuild and restart the frontend service
|
||||
ssh $VPS_USER@$VPS_IP "cd $REMOTE_PATH/backend && docker compose -f docker-compose.gemini.yml up -d --build frontend"
|
||||
|
||||
echo "Deployment complete! Please refresh your browser."
|
||||
|
|
@ -9,6 +9,10 @@ COPY package*.json ./
|
|||
# Install dependencies
|
||||
RUN npm ci
|
||||
|
||||
# Build arguments
|
||||
ARG NEXT_PUBLIC_API_BASE_URL
|
||||
ENV NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_API_BASE_URL
|
||||
|
||||
# Copy source
|
||||
COPY . .
|
||||
|
||||
|
|
|
|||
48
web/app/(app)/community/page.tsx
Normal file
48
web/app/(app)/community/page.tsx
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
import React from 'react'
|
||||
|
||||
export default function CommunityPage() {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-gray-900 dark:text-white font-heading">
|
||||
Community
|
||||
</h1>
|
||||
<p className="mt-2 text-gray-600 dark:text-gray-400">
|
||||
Connect with others, join support groups, and share your journey.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{/* Placeholder for community features */}
|
||||
<div className="bg-white dark:bg-gray-800 overflow-hidden shadow rounded-lg">
|
||||
<div className="p-5">
|
||||
<div className="flex items-center">
|
||||
<div className="flex-shrink-0">
|
||||
{/* Icon */}
|
||||
</div>
|
||||
<div className="ml-5 w-0 flex-1">
|
||||
<dl>
|
||||
<dt className="text-sm font-medium text-gray-500 truncate">
|
||||
Support Groups
|
||||
</dt>
|
||||
<dd>
|
||||
<div className="text-lg font-medium text-gray-900 dark:text-white">
|
||||
Join a Group
|
||||
</div>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-gray-50 dark:bg-gray-900 px-5 py-3">
|
||||
<div className="text-sm">
|
||||
<a href="/supportgroup" className="font-medium text-primary-600 hover:text-primary-500">
|
||||
View all groups
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
54
web/app/(app)/dashboard/page.tsx
Normal file
54
web/app/(app)/dashboard/page.tsx
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
'use client'
|
||||
|
||||
import { useAuth } from '@/lib/hooks/useAuth'
|
||||
import { Link } from '@/components/common/Link'
|
||||
|
||||
export default function DashboardPage() {
|
||||
const { user } = useAuth()
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="bg-white shadow rounded-lg p-6">
|
||||
<h1 className="text-2xl font-bold text-gray-900">
|
||||
Welcome back, {user?.display_name || user?.email?.split('@')[0] || 'Friend'}!
|
||||
</h1>
|
||||
<p className="mt-2 text-gray-600">
|
||||
We're glad you're here. This is your personal space to connect, learn, and grow.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{/* Quick Links */}
|
||||
<div className="bg-white shadow rounded-lg p-6">
|
||||
<h2 className="text-lg font-medium text-gray-900 mb-4">Community</h2>
|
||||
<p className="text-gray-600 mb-4">
|
||||
Join the conversation in our forums. Connect with others who understand your journey.
|
||||
</p>
|
||||
<Link href="https://forum.morethanadiagnosis.org" variant="primary">
|
||||
Go to Forum
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="bg-white shadow rounded-lg p-6">
|
||||
<h2 className="text-lg font-medium text-gray-900 mb-4">Resources</h2>
|
||||
<p className="text-gray-600 mb-4">
|
||||
Explore our curated collection of resources to support you.
|
||||
</p>
|
||||
<Link href="/resources" variant="primary">
|
||||
Browse Resources
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="bg-white shadow rounded-lg p-6">
|
||||
<h2 className="text-lg font-medium text-gray-900 mb-4">The Journal</h2>
|
||||
<p className="text-gray-600 mb-4">
|
||||
Read the latest stories and insights from our community blog.
|
||||
</p>
|
||||
<Link href="/thejournal" variant="primary">
|
||||
Read Blog
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
48
web/app/(app)/layout.tsx
Normal file
48
web/app/(app)/layout.tsx
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
'use client'
|
||||
|
||||
import { useEffect } from 'react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useAuth } from '@/lib/hooks/useAuth'
|
||||
import { Sidebar } from '@/components/layout/Sidebar'
|
||||
import { MobileNav } from '@/components/layout/MobileNav'
|
||||
|
||||
export default function AppLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
const { isAuthenticated, isLoading } = useAuth()
|
||||
const router = useRouter()
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoading && !isAuthenticated) {
|
||||
router.push('/login')
|
||||
}
|
||||
}, [isLoading, isAuthenticated, router])
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-screen bg-gray-50">
|
||||
<div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-primary-600"></div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (!isAuthenticated) {
|
||||
return null // Will redirect
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
<Sidebar />
|
||||
<div className="md:pl-64 flex flex-col min-h-screen">
|
||||
<main className="flex-1 pb-20 md:pb-0">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
{children}
|
||||
</div>
|
||||
</main>
|
||||
<MobileNav />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
108
web/app/(app)/podcast/page.tsx
Normal file
108
web/app/(app)/podcast/page.tsx
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
interface PodcastEpisode {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
audio_url: string;
|
||||
published_at: string;
|
||||
duration: string;
|
||||
image_url: string;
|
||||
}
|
||||
|
||||
interface PodcastFeed {
|
||||
title: string;
|
||||
description: string;
|
||||
image_url: string;
|
||||
episodes: PodcastEpisode[];
|
||||
}
|
||||
|
||||
const API_URL = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://216.158.230.94:8001/api/v1';
|
||||
|
||||
export default function PodcastPage() {
|
||||
const [feed, setFeed] = useState<PodcastFeed | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchPodcasts = async () => {
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/podcast/`);
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch podcasts');
|
||||
}
|
||||
const data = await response.json();
|
||||
setFeed(data);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'An error occurred');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchPodcasts();
|
||||
}, []);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex justify-center items-center min-h-screen bg-background">
|
||||
<div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-primary"></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="flex justify-center items-center min-h-screen bg-background">
|
||||
<div className="text-red-500 text-xl">Error: {error}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background text-text p-8">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<header className="mb-12 text-center">
|
||||
<h1 className="text-4xl font-heading font-bold text-primary mb-4">Podcasts</h1>
|
||||
<p className="text-lg text-text opacity-80 font-body">Listen to our latest episodes and stories.</p>
|
||||
</header>
|
||||
|
||||
<div className="space-y-8">
|
||||
{feed?.episodes.map((episode) => (
|
||||
<div key={episode.id} className="bg-surface rounded-xl shadow-card p-6 flex flex-col md:flex-row gap-6 transition-transform hover:scale-[1.01]">
|
||||
<div className="flex-shrink-0">
|
||||
<img
|
||||
src={episode.image_url || feed.image_url}
|
||||
alt={episode.title}
|
||||
className="w-32 h-32 rounded-lg object-cover bg-secondary"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-grow">
|
||||
<h2 className="text-2xl font-heading font-semibold text-primary mb-2">{episode.title}</h2>
|
||||
<div className="flex items-center gap-4 text-sm text-text opacity-60 mb-4 font-body">
|
||||
<span>{new Date(episode.published_at).toLocaleDateString()}</span>
|
||||
<span>•</span>
|
||||
<span>{episode.duration}</span>
|
||||
</div>
|
||||
<div
|
||||
className="text-text opacity-80 mb-6 font-body line-clamp-3"
|
||||
dangerouslySetInnerHTML={{ __html: episode.description }}
|
||||
/>
|
||||
|
||||
<audio
|
||||
controls
|
||||
className="w-full"
|
||||
src={episode.audio_url}
|
||||
>
|
||||
Your browser does not support the audio element.
|
||||
</audio>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
75
web/app/(app)/profile/page.tsx
Normal file
75
web/app/(app)/profile/page.tsx
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
'use client'
|
||||
|
||||
import { useAuth } from '@/lib/hooks/useAuth'
|
||||
import { Button } from '@/components/common/Button'
|
||||
import { UserCircleIcon } from '@heroicons/react/24/solid'
|
||||
|
||||
export default function ProfilePage() {
|
||||
const { user, logout } = useAuth()
|
||||
|
||||
if (!user) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-gray-900 dark:text-white font-heading">
|
||||
My Profile
|
||||
</h1>
|
||||
<p className="mt-2 text-gray-600 dark:text-gray-400">
|
||||
Manage your account settings and preferences.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white dark:bg-gray-800 shadow rounded-lg overflow-hidden">
|
||||
<div className="px-4 py-5 sm:px-6 border-b border-gray-200 dark:border-gray-700">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900 dark:text-white">
|
||||
User Information
|
||||
</h3>
|
||||
</div>
|
||||
<div className="px-4 py-5 sm:p-6">
|
||||
<div className="flex items-center mb-6">
|
||||
<div className="h-20 w-20 rounded-full bg-primary-100 flex items-center justify-center text-primary-600">
|
||||
<UserCircleIcon className="h-16 w-16" />
|
||||
</div>
|
||||
<div className="ml-6">
|
||||
<h2 className="text-xl font-bold text-gray-900 dark:text-white">
|
||||
{user.display_name || 'Community Member'}
|
||||
</h2>
|
||||
<p className="text-gray-500 dark:text-gray-400">{user.email}</p>
|
||||
{user.is_verified && (
|
||||
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800 mt-2">
|
||||
Verified Member
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-gray-200 dark:border-gray-700 pt-6">
|
||||
<dl className="grid grid-cols-1 gap-x-4 gap-y-6 sm:grid-cols-2">
|
||||
<div className="sm:col-span-1">
|
||||
<dt className="text-sm font-medium text-gray-500 dark:text-gray-400">
|
||||
User ID
|
||||
</dt>
|
||||
<dd className="mt-1 text-sm text-gray-900 dark:text-white font-mono">
|
||||
{user.id}
|
||||
</dd>
|
||||
</div>
|
||||
{/* Add more fields here as needed */}
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-gray-50 dark:bg-gray-900 px-4 py-4 sm:px-6 flex justify-end">
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => logout()}
|
||||
className="text-red-600 hover:text-red-700 border-red-200 hover:border-red-300 hover:bg-red-50"
|
||||
>
|
||||
Sign Out
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
120
web/app/(app)/thejournal/page.tsx
Normal file
120
web/app/(app)/thejournal/page.tsx
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
'use client'
|
||||
import Link from 'next/link'
|
||||
import { useEffect } from 'react'
|
||||
import { useApi } from '@/lib/hooks/useApi'
|
||||
import { LoadingState } from '@/components/ui/LoadingState'
|
||||
import { ErrorState } from '@/components/ui/ErrorState'
|
||||
|
||||
interface BlogPost {
|
||||
id: string
|
||||
title: string
|
||||
link: string
|
||||
published_at: string
|
||||
summary: string
|
||||
image_url?: string
|
||||
author?: string
|
||||
}
|
||||
|
||||
interface BlogResponse {
|
||||
title: string
|
||||
description: string
|
||||
items: BlogPost[]
|
||||
}
|
||||
export default function TheJournalPage() {
|
||||
const { data, error, isLoading, execute } = useApi<BlogResponse>()
|
||||
|
||||
useEffect(() => {
|
||||
execute({
|
||||
url: '/blog/rss',
|
||||
method: 'GET',
|
||||
})
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<main className="min-h-screen bg-white">
|
||||
<header className="sticky top-0 z-50 bg-white shadow-sm border-b border-gray-200">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<Link href="/" className="text-2xl font-bold text-gray-900">MoreThanADiagnosis</Link>
|
||||
<nav className="hidden md:flex gap-8">
|
||||
<Link href="/" className="text-gray-600 hover:text-blue-600">Home</Link>
|
||||
<Link href="/thejournal" className="text-gray-600 hover:text-blue-600 font-semibold text-blue-600">The Journal</Link>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
||||
{isLoading && <LoadingState message="Loading journal entries..." />}
|
||||
|
||||
{error && <ErrorState message="Failed to load journal entries." onRetry={() => window.location.reload()} />}
|
||||
|
||||
{!isLoading && !error && (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-gray-900 dark:text-white font-heading">
|
||||
The Journal
|
||||
</h1>
|
||||
<p className="mt-2 text-gray-600 dark:text-gray-400">
|
||||
Reflections, stories, and updates from our community.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-8 md:grid-cols-2 lg:grid-cols-3">
|
||||
{data?.items.map((post) => (
|
||||
<article
|
||||
key={post.id}
|
||||
className="flex flex-col bg-white dark:bg-gray-800 rounded-lg shadow-lg overflow-hidden hover:shadow-xl transition-shadow duration-300"
|
||||
>
|
||||
{post.image_url && (
|
||||
<div className="flex-shrink-0 h-48 w-full relative">
|
||||
<img
|
||||
className="h-full w-full object-cover"
|
||||
src={post.image_url}
|
||||
alt={post.title}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex-1 p-6 flex flex-col justify-between">
|
||||
<div className="flex-1">
|
||||
<p className="text-sm font-medium text-primary-600">
|
||||
{post.author || 'MoreThanADiagnosis'}
|
||||
</p>
|
||||
<a href={post.link} target="_blank" rel="noopener noreferrer" className="block mt-2">
|
||||
<p className="text-xl font-semibold text-gray-900 dark:text-white">
|
||||
{post.title}
|
||||
</p>
|
||||
<div
|
||||
className="mt-3 text-base text-gray-500 dark:text-gray-400 line-clamp-3"
|
||||
dangerouslySetInnerHTML={{ __html: post.summary || '' }}
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
<div className="mt-6 flex items-center">
|
||||
<div className="flex-shrink-0">
|
||||
<span className="sr-only">{post.published_at}</span>
|
||||
</div>
|
||||
<div className="text-sm text-gray-500 dark:text-gray-400">
|
||||
<time dateTime={post.published_at}>
|
||||
{new Date(post.published_at).toLocaleDateString(undefined, {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
})}
|
||||
</time>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<footer className="bg-gray-900 text-white py-12 border-t border-gray-800">
|
||||
<div className="max-w-7xl mx-auto px-4 text-center">
|
||||
<p>© 2025 More Than A Diagnosis. All rights reserved.</p>
|
||||
</div>
|
||||
</footer>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
|
@ -139,7 +139,7 @@ export default function LoginPage() {
|
|||
|
||||
<div className="text-center text-sm text-gray-600 dark:text-gray-400">
|
||||
Don't have an account?{' '}
|
||||
<Link href="/auth/signup" variant="primary">
|
||||
<Link href="/signup" variant="primary">
|
||||
Sign up
|
||||
</Link>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ export default function ResetPasswordPage() {
|
|||
</p>
|
||||
|
||||
<div className="pt-4">
|
||||
<Link href="/auth/login" variant="primary">
|
||||
<Link href="/login" variant="primary">
|
||||
<Button variant="ghost" fullWidth>
|
||||
Back to login
|
||||
</Button>
|
||||
|
|
@ -134,7 +134,7 @@ export default function ResetPasswordPage() {
|
|||
|
||||
<div className="text-center text-sm text-gray-600 dark:text-gray-400">
|
||||
Remember your password?{' '}
|
||||
<Link href="/auth/login" variant="primary">
|
||||
<Link href="/login" variant="primary">
|
||||
Sign in
|
||||
</Link>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@ export default function SignupPage() {
|
|||
|
||||
<div className="text-center text-sm text-gray-600 dark:text-gray-400">
|
||||
Already have an account?{' '}
|
||||
<Link href="/auth/login" variant="primary">
|
||||
<Link href="/login" variant="primary">
|
||||
Sign in
|
||||
</Link>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,30 +3,35 @@
|
|||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
/* Light mode colors */
|
||||
--color-primary: #3B82F6;
|
||||
--color-secondary: #8B5CF6;
|
||||
/* Compassionate Theme Colors */
|
||||
--color-primary: #5D7B6F; /* Sage */
|
||||
--color-secondary: #E6DACE; /* Sand */
|
||||
--color-accent: #D4A373; /* Muted Coral */
|
||||
|
||||
--color-error: #EF4444;
|
||||
--color-success: #10B981;
|
||||
--color-warning: #F59E0B;
|
||||
|
||||
--color-bg: #FFFFFF;
|
||||
--color-bg-secondary: #F9FAFB;
|
||||
--color-text: #111827;
|
||||
--color-text-secondary: #6B7280;
|
||||
--color-border: #D1D5DB;
|
||||
--color-bg: #FAF9F6; /* Cream */
|
||||
--color-bg-secondary: #FFFFFF; /* White */
|
||||
--color-text: #2C3333; /* Charcoal */
|
||||
--color-text-secondary: #6B7280; /* Muted Text */
|
||||
--color-border: #E5E7EB;
|
||||
|
||||
/* Spacing base unit */
|
||||
--spacing-unit: 4px;
|
||||
}
|
||||
|
||||
.dark {
|
||||
/* Dark mode colors */
|
||||
--color-bg: #1F2937;
|
||||
--color-bg-secondary: #111827;
|
||||
--color-text: #F9FAFB;
|
||||
--color-text-secondary: #D1D5DB;
|
||||
--color-border: #4B5563;
|
||||
/* Dark mode colors - adapted for softness */
|
||||
--color-bg: #1A1D1D; /* Dark Charcoal */
|
||||
--color-bg-secondary: #2C3333;
|
||||
--color-text: #FAF9F6; /* Cream Text */
|
||||
--color-text-secondary: #9CA3AF;
|
||||
--color-border: #374151;
|
||||
|
||||
--color-primary: #749688; /* Lighter Sage */
|
||||
--color-secondary: #5D4E40; /* Darker Sand */
|
||||
}
|
||||
|
||||
* {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,19 @@
|
|||
import type { Metadata } from 'next'
|
||||
import { Lora, Source_Sans_3 } from 'next/font/google'
|
||||
import './globals.css'
|
||||
|
||||
const lora = Lora({
|
||||
subsets: ['latin'],
|
||||
variable: '--font-lora',
|
||||
display: 'swap',
|
||||
})
|
||||
|
||||
const sourceSans = Source_Sans_3({
|
||||
subsets: ['latin'],
|
||||
variable: '--font-source-sans',
|
||||
display: 'swap',
|
||||
})
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'MoreThanADiagnosis',
|
||||
description: 'Community platform for health advocacy and support',
|
||||
|
|
@ -12,8 +25,8 @@ export default function RootLayout({
|
|||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className="font-sans antialiased">{children}</body>
|
||||
<html lang="en" className={`${lora.variable} ${sourceSans.variable}`}>
|
||||
<body className="font-sans antialiased bg-background text-text">{children}</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
231
web/app/page.tsx
231
web/app/page.tsx
|
|
@ -4,96 +4,113 @@ import Link from 'next/link'
|
|||
|
||||
export default function Home() {
|
||||
return (
|
||||
<main className="min-h-screen bg-white">
|
||||
<main className="min-h-screen bg-background text-text font-sans selection:bg-primary-200 selection:text-primary-900">
|
||||
{/* Header */}
|
||||
<header className="sticky top-0 z-50 bg-white shadow-sm border-b border-gray-200">
|
||||
<header className="sticky top-0 z-50 bg-background/80 backdrop-blur-md border-b border-primary-100">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<Link href="/" className="text-2xl font-bold text-gray-900">
|
||||
<Link href="/" className="text-2xl font-heading font-bold text-primary-700 hover:text-primary-600 transition-colors">
|
||||
MoreThanADiagnosis
|
||||
</Link>
|
||||
<nav className="hidden md:flex gap-8">
|
||||
<Link href="/" className="text-gray-600 hover:text-blue-600 transition-colors">Home</Link>
|
||||
<Link href="/podcast" className="text-gray-600 hover:text-blue-600 transition-colors">Podcast</Link>
|
||||
<Link href="/resources" className="text-gray-600 hover:text-blue-600 transition-colors">Resources</Link>
|
||||
<Link href="/happymail" className="text-gray-600 hover:text-blue-600 transition-colors">Happy Mail</Link>
|
||||
<Link href="/supportgroup" className="text-gray-600 hover:text-blue-600 transition-colors">Support Group</Link>
|
||||
<Link href="/shop" className="text-gray-600 hover:text-blue-600 transition-colors">Shop</Link>
|
||||
<Link href="/" className="text-text-muted hover:text-primary-600 transition-colors font-medium">Home</Link>
|
||||
<Link href="/podcast" className="text-text-muted hover:text-primary-600 transition-colors font-medium">Podcast</Link>
|
||||
<Link href="/resources" className="text-text-muted hover:text-primary-600 transition-colors font-medium">Resources</Link>
|
||||
<Link href="/happymail" className="text-text-muted hover:text-primary-600 transition-colors font-medium">Happy Mail</Link>
|
||||
<Link href="/supportgroup" className="text-text-muted hover:text-primary-600 transition-colors font-medium">Support Group</Link>
|
||||
<Link href="/shop" className="text-text-muted hover:text-primary-600 transition-colors font-medium">Shop</Link>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Hero Section */}
|
||||
<section className="bg-gradient-to-br from-blue-50 via-white to-purple-50 py-20">
|
||||
<section className="relative overflow-hidden py-24 lg:py-32">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-primary-50 via-background to-secondary-50 opacity-70 -z-10" />
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="text-center">
|
||||
<h1 className="text-6xl md:text-7xl font-bold text-gray-900 mb-6">
|
||||
You are more than a diagnosis.
|
||||
<div className="text-center max-w-4xl mx-auto">
|
||||
<h1 className="text-5xl md:text-7xl font-heading font-bold text-primary-800 mb-8 leading-tight">
|
||||
You are more than <br className="hidden md:block" /> a diagnosis.
|
||||
</h1>
|
||||
<h2 className="text-2xl md:text-3xl text-gray-700 mb-8">
|
||||
Connecting Through Stories, Thriving Through Community
|
||||
<h2 className="text-2xl md:text-3xl text-text-muted mb-10 font-light leading-relaxed">
|
||||
Connecting Through Stories, <span className="text-accent font-medium">Thriving Through Community</span>
|
||||
</h2>
|
||||
<p className="text-xl text-gray-600 max-w-3xl mx-auto mb-12">
|
||||
More Than A Diagnosis is a place for connection, encouragement, support, and resources by and for folks living with chronic illness and those touched by cancer. Join us on a journey where we explore the human experience beyond medical labels. Discover inspiring stories, insightful conversations, and valuable resources that redefine what it means to live with a diagnosis.
|
||||
<p className="text-xl text-text-muted max-w-2xl mx-auto mb-12 leading-relaxed">
|
||||
A sanctuary for connection, encouragement, and support. Join us on a journey where we explore the human experience beyond medical labels.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<Link
|
||||
href="/supportgroup"
|
||||
className="inline-block px-8 py-4 bg-blue-600 text-white font-semibold rounded-lg hover:bg-blue-700 transition-colors shadow-lg"
|
||||
className="inline-flex items-center justify-center px-8 py-4 bg-primary text-white font-semibold rounded-full hover:bg-primary-600 transition-all shadow-lg hover:shadow-xl hover:-translate-y-0.5"
|
||||
>
|
||||
Join Our Community
|
||||
</Link>
|
||||
<Link
|
||||
href="/resources"
|
||||
className="inline-flex items-center justify-center px-8 py-4 bg-white text-primary-700 font-semibold rounded-full border-2 border-primary-100 hover:border-primary-300 hover:bg-primary-50 transition-all"
|
||||
>
|
||||
Explore Resources
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Happy Mail Section */}
|
||||
<section className="py-20 bg-white border-t border-gray-200">
|
||||
<section className="py-24 bg-white">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="grid md:grid-cols-2 gap-12 items-center">
|
||||
<div>
|
||||
<h3 className="text-4xl font-bold text-gray-900 mb-6">Happy Mail</h3>
|
||||
<p className="text-xl text-gray-600 mb-4">
|
||||
Happy Mail is a small way to remind you: you're seen, supported, and not alone.
|
||||
</p>
|
||||
<p className="text-lg text-gray-600 mb-6">
|
||||
Nerisa sends free, joy-filled snail mail to folks navigating the hard stuff - just because.
|
||||
<div className="grid md:grid-cols-2 gap-16 items-center">
|
||||
<div className="order-2 md:order-1">
|
||||
<div className="bg-secondary-50 rounded-3xl p-10 md:p-14 relative overflow-hidden">
|
||||
<div className="absolute top-0 right-0 w-64 h-64 bg-secondary-100 rounded-full -mr-32 -mt-32 opacity-50" />
|
||||
<div className="absolute bottom-0 left-0 w-48 h-48 bg-secondary-200 rounded-full -ml-24 -mb-24 opacity-50" />
|
||||
<div className="relative z-10 text-center">
|
||||
<div className="text-8xl mb-6 animate-bounce-slow">💌</div>
|
||||
<p className="text-secondary-800 font-heading font-bold text-xl">From the Worst Club's Best Members</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="order-1 md:order-2">
|
||||
<span className="text-accent font-bold tracking-wider uppercase text-sm mb-2 block">Spreading Joy</span>
|
||||
<h3 className="text-4xl font-heading font-bold text-primary-800 mb-6">Happy Mail</h3>
|
||||
<p className="text-xl text-text-muted mb-6 leading-relaxed">
|
||||
Happy Mail is a small way to remind you: you're seen, supported, and not alone. Nerisa sends free, joy-filled snail mail to folks navigating the hard stuff - just because.
|
||||
</p>
|
||||
|
||||
<div className="mb-8">
|
||||
<h4 className="text-lg font-semibold text-gray-900 mb-4">Who Can Receive Happy Mail?</h4>
|
||||
<ul className="space-y-2 text-gray-700">
|
||||
<li>✓ Cancer diagnosis or treatment</li>
|
||||
<li>✓ Chronic illness or rare disease</li>
|
||||
<li>✓ Medical limbo or recovery</li>
|
||||
<div className="mb-10 bg-primary-50 rounded-2xl p-6 border border-primary-100">
|
||||
<h4 className="text-lg font-bold text-primary-800 mb-4">Who Can Receive Happy Mail?</h4>
|
||||
<ul className="space-y-3 text-text-muted">
|
||||
{['Cancer diagnosis or treatment', 'Chronic illness or rare disease', 'Medical limbo or recovery'].map((item, i) => (
|
||||
<li key={i} className="flex items-center gap-3">
|
||||
<span className="flex-shrink-0 w-6 h-6 rounded-full bg-success text-white flex items-center justify-center text-sm">✓</span>
|
||||
{item}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<Link
|
||||
href="/happymail"
|
||||
className="inline-block px-6 py-3 bg-blue-600 text-white font-semibold rounded-lg hover:bg-blue-700 transition-colors"
|
||||
className="inline-flex items-center px-6 py-3 bg-secondary text-secondary-900 font-semibold rounded-full hover:bg-secondary-400 transition-colors"
|
||||
>
|
||||
Order Happy Mail
|
||||
</Link>
|
||||
</div>
|
||||
<div className="bg-gradient-to-br from-pink-100 to-purple-100 rounded-lg p-8 text-center">
|
||||
<div className="text-6xl mb-4">💌</div>
|
||||
<p className="text-gray-700 font-semibold">From the Worst Club's Best Members</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Connect Section */}
|
||||
<section className="py-20 bg-gradient-to-br from-blue-600 to-purple-600 text-white">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
|
||||
<h3 className="text-4xl font-bold mb-8">Connect</h3>
|
||||
<blockquote className="text-2xl mb-8 italic">
|
||||
<section className="py-24 bg-primary-700 text-white relative overflow-hidden">
|
||||
<div className="absolute inset-0 bg-[url('/pattern.svg')] opacity-5" />
|
||||
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 text-center relative z-10">
|
||||
<h3 className="text-4xl font-heading font-bold mb-10">Connect</h3>
|
||||
<blockquote className="text-2xl md:text-3xl font-serif italic mb-12 leading-relaxed opacity-90">
|
||||
"We're here to create a safe, supportive space where you can connect with others, share your story, and find hope. Cancer and chronic illness can feel so isolating, but together, we're stronger."
|
||||
</blockquote>
|
||||
<Link
|
||||
href="/supportgroup"
|
||||
className="inline-block px-8 py-4 bg-white text-blue-600 font-semibold rounded-lg hover:bg-gray-100 transition-colors"
|
||||
className="inline-block px-10 py-4 bg-white text-primary-800 font-bold rounded-full hover:bg-primary-50 transition-all shadow-lg hover:scale-105"
|
||||
>
|
||||
Learn More
|
||||
</Link>
|
||||
|
|
@ -101,26 +118,27 @@ export default function Home() {
|
|||
</section>
|
||||
|
||||
{/* Podcast Section */}
|
||||
<section className="py-20 bg-white border-t border-gray-200">
|
||||
<section className="py-24 bg-background">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<h3 className="text-4xl font-bold text-gray-900 mb-12 text-center">Podcast</h3>
|
||||
<div className="grid md:grid-cols-2 gap-12 items-center">
|
||||
<div className="bg-gradient-to-br from-orange-100 to-pink-100 rounded-lg p-8 text-center">
|
||||
<div className="text-6xl mb-4">🎙️</div>
|
||||
<p className="text-gray-700 font-semibold">More Than A Diagnosis Podcast</p>
|
||||
<div className="text-center mb-16">
|
||||
<h3 className="text-4xl font-heading font-bold text-primary-800 mb-4">The Podcast</h3>
|
||||
<p className="text-text-muted max-w-2xl mx-auto">Real conversations about life beyond the medical chart.</p>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-2 gap-12 items-center bg-white rounded-3xl p-8 md:p-12 shadow-sm border border-primary-50">
|
||||
<div className="bg-gradient-to-br from-accent/20 to-secondary/20 rounded-2xl p-12 text-center aspect-square flex items-center justify-center">
|
||||
<div className="text-8xl">🎙️</div>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-lg text-gray-600 mb-6">
|
||||
Listen to More Than A Diagnosis where we get real about life beyond the medical chart.
|
||||
</p>
|
||||
<p className="text-gray-700 mb-8">
|
||||
Hosts Jes and Den are lifelong friends who found their way back to each other thanks to the wild ride of cancer and chronic illness. Jes went through triple-negative breast cancer and now navigates the long-term side effects of treatment. Den lives with FAP, a rare genetic condition, alongside other chronic illnesses. Through it all, they've found the magic of community, connection, and telling the real, messy stories.
|
||||
<h4 className="text-2xl font-heading font-bold text-primary-800 mb-6">More Than A Diagnosis</h4>
|
||||
<p className="text-text-muted mb-8 leading-relaxed">
|
||||
Hosts Jes and Den are lifelong friends who found their way back to each other thanks to the wild ride of cancer and chronic illness. Jes went through triple-negative breast cancer and now navigates the long-term side effects of treatment. Den lives with FAP, a rare genetic condition. Through it all, they've found the magic of community.
|
||||
</p>
|
||||
<Link
|
||||
href="/podcast"
|
||||
className="inline-block px-6 py-3 bg-blue-600 text-white font-semibold rounded-lg hover:bg-blue-700 transition-colors"
|
||||
className="inline-flex items-center px-8 py-3 bg-primary text-white font-semibold rounded-full hover:bg-primary-600 transition-colors"
|
||||
>
|
||||
Listen Here
|
||||
Listen Now
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -128,106 +146,107 @@ export default function Home() {
|
|||
</section>
|
||||
|
||||
{/* Resources Section */}
|
||||
<section className="py-20 bg-gray-50 border-t border-gray-200">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<h3 className="text-4xl font-bold text-gray-900 mb-8 text-center">Resources</h3>
|
||||
<p className="text-xl text-gray-600 max-w-3xl mx-auto mb-12 text-center">
|
||||
We know how scary and overwhelming it can be to receive a diagnosis, not to mention the financial burden it can bring. That's why we've put together a list of helpful resources to attempt to make this journey even just a little bit easier for you. Be sure to check back often or sign up to receive updates as we are adding new resources all the time!
|
||||
<section className="py-24 bg-white">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
|
||||
<h3 className="text-4xl font-heading font-bold text-primary-800 mb-8">Resources</h3>
|
||||
<p className="text-xl text-text-muted max-w-3xl mx-auto mb-12 leading-relaxed">
|
||||
We know how scary and overwhelming it can be to receive a diagnosis. We've put together a list of helpful resources to attempt to make this journey even just a little bit easier for you.
|
||||
</p>
|
||||
<div className="text-center">
|
||||
<Link
|
||||
href="/resources"
|
||||
className="inline-block px-8 py-4 bg-blue-600 text-white font-semibold rounded-lg hover:bg-blue-700 transition-colors"
|
||||
className="inline-block px-8 py-4 bg-secondary text-secondary-900 font-semibold rounded-full hover:bg-secondary-400 transition-colors"
|
||||
>
|
||||
View Resources
|
||||
Browse Resources
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Wings of Remembrance Section */}
|
||||
<section className="py-20 bg-white border-t border-gray-200">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<h3 className="text-4xl font-bold text-gray-900 mb-8 text-center">Wings of Remembrance</h3>
|
||||
<p className="text-xl text-gray-600 max-w-3xl mx-auto mb-12 text-center">
|
||||
<section className="py-24 bg-primary-50">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
|
||||
<span className="text-accent font-bold tracking-wider uppercase text-sm mb-2 block">Honoring Legacies</span>
|
||||
<h3 className="text-4xl font-heading font-bold text-primary-800 mb-8">Wings of Remembrance</h3>
|
||||
<p className="text-xl text-text-muted max-w-3xl mx-auto mb-12 leading-relaxed">
|
||||
We invite you to share a tribute—a memory, message, or reflection—to honor those who shaped our journey. Together, we create a tapestry of wings that celebrates their legacy.
|
||||
</p>
|
||||
<div className="text-center">
|
||||
<Link
|
||||
href="/inlovingmemory"
|
||||
className="inline-block px-8 py-4 bg-blue-600 text-white font-semibold rounded-lg hover:bg-blue-700 transition-colors"
|
||||
className="inline-block px-8 py-4 bg-white text-primary-700 font-semibold rounded-full border border-primary-200 hover:bg-primary-50 transition-colors shadow-sm"
|
||||
>
|
||||
Share Your Tribute
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Shop Section */}
|
||||
<section className="py-20 bg-gray-50 border-t border-gray-200">
|
||||
<section className="py-24 bg-white">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<h3 className="text-4xl font-bold text-gray-900 mb-12 text-center">Shop Our Collections</h3>
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-4 gap-8 mb-12">
|
||||
<div className="text-center mb-16">
|
||||
<h3 className="text-4xl font-heading font-bold text-primary-800 mb-4">Shop Our Collections</h3>
|
||||
<p className="text-text-muted">Wear your story with pride.</p>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-4 gap-6 mb-12">
|
||||
{[
|
||||
{ name: 'Worst Club Best Members', icon: '🦆' },
|
||||
{ name: 'More Than A Diagnosis', icon: '💪' },
|
||||
{ name: 'I Don\'t Want To I Get To', icon: '✨' },
|
||||
{ name: 'Ribbon Collection', icon: '🎗️' }
|
||||
{ name: 'Worst Club Best Members', icon: '🦆', color: 'bg-primary-50' },
|
||||
{ name: 'More Than A Diagnosis', icon: '💪', color: 'bg-secondary-50' },
|
||||
{ name: 'I Don\'t Want To I Get To', icon: '✨', color: 'bg-accent/10' },
|
||||
{ name: 'Ribbon Collection', icon: '🎗️', color: 'bg-primary-100' }
|
||||
].map((collection) => (
|
||||
<div key={collection.name} className="bg-white rounded-lg p-6 text-center hover:shadow-lg transition-shadow">
|
||||
<div className="text-4xl mb-4">{collection.icon}</div>
|
||||
<h4 className="text-lg font-semibold text-gray-900">{collection.name}</h4>
|
||||
<div key={collection.name} className={`${collection.color} rounded-2xl p-8 text-center hover:shadow-md transition-all hover:-translate-y-1 cursor-pointer group`}>
|
||||
<div className="text-5xl mb-6 group-hover:scale-110 transition-transform duration-300">{collection.icon}</div>
|
||||
<h4 className="text-lg font-bold text-primary-900">{collection.name}</h4>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<Link
|
||||
href="/shop"
|
||||
className="inline-block px-8 py-4 bg-blue-600 text-white font-semibold rounded-lg hover:bg-blue-700 transition-colors"
|
||||
className="inline-block px-8 py-4 bg-primary text-white font-semibold rounded-full hover:bg-primary-600 transition-colors"
|
||||
>
|
||||
Shop Now
|
||||
Visit Shop
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Footer */}
|
||||
<footer className="bg-gray-900 text-white py-12 border-t border-gray-800">
|
||||
<footer className="bg-primary-900 text-white py-16 border-t border-primary-800">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="grid md:grid-cols-4 gap-8 mb-8">
|
||||
<div className="grid md:grid-cols-4 gap-12 mb-12">
|
||||
<div>
|
||||
<h4 className="font-bold mb-4">Navigation</h4>
|
||||
<ul className="space-y-2">
|
||||
<li><Link href="/" className="hover:text-blue-400">Home</Link></li>
|
||||
<li><Link href="/podcast" className="hover:text-blue-400">Podcast</Link></li>
|
||||
<li><Link href="/resources" className="hover:text-blue-400">Resources</Link></li>
|
||||
<h4 className="font-heading font-bold text-lg mb-6 text-primary-100">Navigation</h4>
|
||||
<ul className="space-y-3">
|
||||
<li><Link href="/" className="text-primary-200 hover:text-white transition-colors">Home</Link></li>
|
||||
<li><Link href="/podcast" className="text-primary-200 hover:text-white transition-colors">Podcast</Link></li>
|
||||
<li><Link href="/resources" className="text-primary-200 hover:text-white transition-colors">Resources</Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-bold mb-4">Community</h4>
|
||||
<ul className="space-y-2">
|
||||
<li><Link href="/supportgroup" className="hover:text-blue-400">Support Group</Link></li>
|
||||
<li><Link href="/groups" className="hover:text-blue-400">Support Circle</Link></li>
|
||||
<li><Link href="/thejournal" className="hover:text-blue-400">The Journal</Link></li>
|
||||
<h4 className="font-heading font-bold text-lg mb-6 text-primary-100">Community</h4>
|
||||
<ul className="space-y-3">
|
||||
<li><Link href="/supportgroup" className="text-primary-200 hover:text-white transition-colors">Support Group</Link></li>
|
||||
<li><Link href="/groups" className="text-primary-200 hover:text-white transition-colors">Support Circle</Link></li>
|
||||
<li><Link href="/thejournal" className="text-primary-200 hover:text-white transition-colors">The Journal</Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-bold mb-4">More</h4>
|
||||
<ul className="space-y-2">
|
||||
<li><Link href="/happymail" className="hover:text-blue-400">Happy Mail</Link></li>
|
||||
<li><Link href="/inlovingmemory" className="hover:text-blue-400">In Loving Memory</Link></li>
|
||||
<li><Link href="/shop" className="hover:text-blue-400">Shop</Link></li>
|
||||
<h4 className="font-heading font-bold text-lg mb-6 text-primary-100">More</h4>
|
||||
<ul className="space-y-3">
|
||||
<li><Link href="/happymail" className="text-primary-200 hover:text-white transition-colors">Happy Mail</Link></li>
|
||||
<li><Link href="/inlovingmemory" className="text-primary-200 hover:text-white transition-colors">In Loving Memory</Link></li>
|
||||
<li><Link href="/shop" className="text-primary-200 hover:text-white transition-colors">Shop</Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-bold mb-4">Connect</h4>
|
||||
<ul className="space-y-2">
|
||||
<li><Link href="/meetus" className="hover:text-blue-400">Meet Us</Link></li>
|
||||
<li><a href="https://www.morethanadiagnosis.org" className="hover:text-blue-400">Original Site</a></li>
|
||||
<h4 className="font-heading font-bold text-lg mb-6 text-primary-100">Connect</h4>
|
||||
<ul className="space-y-3">
|
||||
<li><Link href="/meetus" className="text-primary-200 hover:text-white transition-colors">Meet Us</Link></li>
|
||||
<li><a href="https://www.morethanadiagnosis.org" className="text-primary-200 hover:text-white transition-colors">Original Site</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-t border-gray-800 pt-8 text-center text-gray-400">
|
||||
<div className="border-t border-primary-800 pt-8 text-center text-primary-400">
|
||||
<p>© 2025 More Than A Diagnosis. All rights reserved.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,96 +0,0 @@
|
|||
'use client'
|
||||
|
||||
import Link from 'next/link'
|
||||
|
||||
export default function PodcastPage() {
|
||||
return (
|
||||
<main className="min-h-screen bg-white">
|
||||
{/* Header */}
|
||||
<header className="sticky top-0 z-50 bg-white shadow-sm border-b border-gray-200">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<Link href="/" className="text-2xl font-bold text-gray-900">
|
||||
MoreThanADiagnosis
|
||||
</Link>
|
||||
<nav className="hidden md:flex gap-8">
|
||||
<Link href="/" className="text-gray-600 hover:text-blue-600 transition-colors">Home</Link>
|
||||
<Link href="/podcast" className="text-gray-600 hover:text-blue-600 transition-colors font-semibold text-blue-600">Podcast</Link>
|
||||
<Link href="/resources" className="text-gray-600 hover:text-blue-600 transition-colors">Resources</Link>
|
||||
<Link href="/happymail" className="text-gray-600 hover:text-blue-600 transition-colors">Happy Mail</Link>
|
||||
<Link href="/supportgroup" className="text-gray-600 hover:text-blue-600 transition-colors">Support Group</Link>
|
||||
<Link href="/shop" className="text-gray-600 hover:text-blue-600 transition-colors">Shop</Link>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Hero */}
|
||||
<section className="bg-gradient-to-br from-orange-50 to-pink-50 py-20">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
|
||||
<div className="text-6xl mb-6">🎙️</div>
|
||||
<h1 className="text-5xl font-bold text-gray-900 mb-6">More Than A Diagnosis Podcast</h1>
|
||||
<p className="text-2xl text-gray-700">
|
||||
Getting real about life beyond the medical chart
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Hosts */}
|
||||
<section className="py-20 bg-white border-t border-gray-200">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<h2 className="text-4xl font-bold text-gray-900 mb-12 text-center">Meet Your Hosts</h2>
|
||||
<div className="grid md:grid-cols-2 gap-12">
|
||||
<div className="text-center">
|
||||
<div className="bg-gradient-to-br from-blue-100 to-purple-100 rounded-lg p-12 mb-6">
|
||||
<div className="text-6xl">👩</div>
|
||||
</div>
|
||||
<h3 className="text-2xl font-bold text-gray-900 mb-4">Jes</h3>
|
||||
<p className="text-gray-700 text-lg">
|
||||
Jes went through triple-negative breast cancer and now navigates the long-term side effects of treatment. She's passionate about sharing real, messy stories of survival and resilience.
|
||||
</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="bg-gradient-to-br from-green-100 to-teal-100 rounded-lg p-12 mb-6">
|
||||
<div className="text-6xl">👨</div>
|
||||
</div>
|
||||
<h3 className="text-2xl font-bold text-gray-900 mb-4">Den</h3>
|
||||
<p className="text-gray-700 text-lg">
|
||||
Den lives with FAP, a rare genetic condition, alongside other chronic illnesses. Together with Jes, he creates space for authentic conversations about community and connection.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-12 text-center">
|
||||
<p className="text-xl text-gray-700 max-w-3xl mx-auto">
|
||||
Jes and Den are lifelong friends who found their way back to each other thanks to the wild ride of cancer and chronic illness. Through it all, they've found the magic of community, connection, and telling the real, messy stories.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Episodes */}
|
||||
<section className="py-20 bg-gray-50 border-t border-gray-200">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<h2 className="text-4xl font-bold text-gray-900 mb-12 text-center">Listen Now</h2>
|
||||
<p className="text-xl text-gray-700 text-center max-w-3xl mx-auto mb-12">
|
||||
Available on all major podcast platforms. Subscribe to never miss an episode where we get real about life beyond the medical chart.
|
||||
</p>
|
||||
<div className="text-center">
|
||||
<a
|
||||
href="#"
|
||||
className="inline-block px-8 py-4 bg-blue-600 text-white font-semibold rounded-lg hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
Find on Podcast Apps
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Footer */}
|
||||
<footer className="bg-gray-900 text-white py-12 border-t border-gray-800">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
|
||||
<p>© 2025 More Than A Diagnosis. All rights reserved.</p>
|
||||
</div>
|
||||
</footer>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
'use client'
|
||||
import Link from 'next/link'
|
||||
export default function JournalPage() {
|
||||
return (
|
||||
<main className="min-h-screen bg-white">
|
||||
<header className="sticky top-0 z-50 bg-white shadow-sm border-b border-gray-200">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<Link href="/" className="text-2xl font-bold text-gray-900">MoreThanADiagnosis</Link>
|
||||
<nav className="hidden md:flex gap-8">
|
||||
<Link href="/" className="text-gray-600 hover:text-blue-600">Home</Link>
|
||||
<Link href="/thejournal" className="text-gray-600 hover:text-blue-600 font-semibold text-blue-600">The Journal</Link>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<section className="bg-gradient-to-br from-yellow-50 to-orange-50 py-20">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
|
||||
<div className="text-6xl mb-6">📝</div>
|
||||
<h1 className="text-5xl font-bold text-gray-900 mb-6">The Journal</h1>
|
||||
<p className="text-2xl text-gray-700">Community Stories & Reflections</p>
|
||||
</div>
|
||||
</section>
|
||||
<section className="py-20 bg-white border-t border-gray-200">
|
||||
<div className="max-w-3xl mx-auto px-4">
|
||||
<p className="text-xl text-gray-700 mb-8">Read inspiring stories from our community members as they navigate their journeys with chronic illness and cancer. Share your own story and inspire others.</p>
|
||||
<a href="https://www.morethanadiagnosis.org/thejournal" className="inline-block px-8 py-4 bg-blue-600 text-white font-semibold rounded-lg hover:bg-blue-700">Read Stories</a>
|
||||
</div>
|
||||
</section>
|
||||
<footer className="bg-gray-900 text-white py-12 border-t border-gray-800">
|
||||
<div className="max-w-7xl mx-auto px-4 text-center">
|
||||
<p>© 2025 More Than A Diagnosis. All rights reserved.</p>
|
||||
</div>
|
||||
</footer>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
|
@ -81,7 +81,7 @@ export const Header = ({
|
|||
<Button variant="ghost" size="sm" onClick={onLogin}>
|
||||
Login
|
||||
</Button>
|
||||
<Link href="/auth/signup">
|
||||
<Link href="/signup">
|
||||
<Button variant="primary" size="sm">
|
||||
Sign Up
|
||||
</Button>
|
||||
|
|
@ -175,7 +175,7 @@ export const Header = ({
|
|||
</button>
|
||||
<div onClick={() => setMobileMenuOpen(false)}>
|
||||
<Link
|
||||
href="/auth/signup"
|
||||
href="/signup"
|
||||
variant="neutral"
|
||||
className="block px-4 py-2 text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700 rounded-md no-underline"
|
||||
>
|
||||
|
|
|
|||
46
web/components/layout/MobileNav.tsx
Normal file
46
web/components/layout/MobileNav.tsx
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
'use client'
|
||||
|
||||
import { Link } from '../common/Link'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import {
|
||||
HomeIcon,
|
||||
BookOpenIcon,
|
||||
UserGroupIcon,
|
||||
UserIcon
|
||||
} from '@heroicons/react/24/outline'
|
||||
|
||||
const navigation = [
|
||||
{ name: 'Home', href: '/', icon: HomeIcon },
|
||||
{ name: 'Journal', href: '/thejournal', icon: BookOpenIcon },
|
||||
{ name: 'Forum', href: 'https://forum.morethanadiagnosis.org', icon: UserGroupIcon },
|
||||
{ name: 'Profile', href: '/profile', icon: UserIcon },
|
||||
]
|
||||
|
||||
export function MobileNav() {
|
||||
const pathname = usePathname()
|
||||
|
||||
return (
|
||||
<div className="md:hidden fixed bottom-0 left-0 right-0 bg-white border-t border-gray-200 pb-safe">
|
||||
<div className="flex justify-around items-center h-16">
|
||||
{navigation.map((item) => {
|
||||
const isActive = pathname === item.href
|
||||
return (
|
||||
<Link
|
||||
key={item.name}
|
||||
href={item.href}
|
||||
variant="neutral"
|
||||
className={`flex flex-col items-center justify-center w-full h-full space-y-1 ${isActive ? 'text-primary-600' : 'text-gray-500 hover:text-gray-900'
|
||||
}`}
|
||||
>
|
||||
<item.icon
|
||||
className={`h-6 w-6 ${isActive ? 'text-primary-600' : 'text-gray-400'}`}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span className="text-xs font-medium">{item.name}</span>
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
77
web/components/layout/Sidebar.tsx
Normal file
77
web/components/layout/Sidebar.tsx
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
'use client'
|
||||
|
||||
import { Link } from '../common/Link'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import {
|
||||
HomeIcon,
|
||||
MicrophoneIcon,
|
||||
BookOpenIcon,
|
||||
UserGroupIcon,
|
||||
UserIcon,
|
||||
ArrowRightOnRectangleIcon
|
||||
} from '@heroicons/react/24/outline'
|
||||
import { useAuth } from '@/lib/hooks/useAuth'
|
||||
|
||||
const navigation = [
|
||||
{ name: 'Home', href: '/dashboard', icon: HomeIcon },
|
||||
{ name: 'Podcasts', href: '/podcast', icon: MicrophoneIcon },
|
||||
{ name: 'The Journal', href: '/thejournal', icon: BookOpenIcon },
|
||||
{ name: 'Forum', href: 'https://forum.morethanadiagnosis.org', icon: UserGroupIcon },
|
||||
{ name: 'Resources', href: '/resources', icon: BookOpenIcon },
|
||||
{ name: 'Profile', href: '/profile', icon: UserIcon },
|
||||
]
|
||||
|
||||
export function Sidebar() {
|
||||
const pathname = usePathname()
|
||||
const { logout } = useAuth()
|
||||
|
||||
return (
|
||||
<div className="hidden md:flex md:w-64 md:flex-col md:fixed md:inset-y-0">
|
||||
<div className="flex-1 flex flex-col min-h-0 border-r border-gray-200 bg-white">
|
||||
<div className="flex-1 flex flex-col pt-5 pb-4 overflow-y-auto">
|
||||
<div className="flex items-center flex-shrink-0 px-4 mb-8">
|
||||
<span className="text-xl font-bold text-primary-600">MoreThanADiagnosis</span>
|
||||
</div>
|
||||
<nav className="mt-5 flex-1 px-2 space-y-1">
|
||||
{navigation.map((item) => {
|
||||
const isActive = pathname === item.href
|
||||
return (
|
||||
<Link
|
||||
key={item.name}
|
||||
href={item.href}
|
||||
variant="neutral"
|
||||
className={`group flex items-center px-2 py-2 text-sm font-medium rounded-md transition-colors ${isActive
|
||||
? 'bg-primary-50 text-primary-700'
|
||||
: 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'
|
||||
}`}
|
||||
>
|
||||
<item.icon
|
||||
className={`mr-3 flex-shrink-0 h-6 w-6 ${isActive ? 'text-primary-600' : 'text-gray-400 group-hover:text-gray-500'
|
||||
}`}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{item.name}
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</nav>
|
||||
</div>
|
||||
<div className="flex-shrink-0 flex border-t border-gray-200 p-4">
|
||||
<button
|
||||
onClick={() => logout()}
|
||||
className="flex-shrink-0 w-full group block"
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<ArrowRightOnRectangleIcon className="inline-block h-5 w-5 text-gray-400 group-hover:text-gray-500" />
|
||||
<div className="ml-3">
|
||||
<p className="text-sm font-medium text-gray-700 group-hover:text-gray-900">
|
||||
Sign out
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -19,12 +19,12 @@ export const DashboardLayout = ({ children }: DashboardLayoutProps) => {
|
|||
// Redirect to login if not authenticated
|
||||
React.useEffect(() => {
|
||||
if (!isAuthenticated) {
|
||||
router.push('/auth/login')
|
||||
router.push('/login')
|
||||
}
|
||||
}, [isAuthenticated, router])
|
||||
|
||||
const handleLogin = () => {
|
||||
router.push('/auth/login')
|
||||
router.push('/login')
|
||||
}
|
||||
|
||||
const sidebarItems = [
|
||||
|
|
@ -62,8 +62,7 @@ export const DashboardLayout = ({ children }: DashboardLayoutProps) => {
|
|||
variant="neutral"
|
||||
className={`
|
||||
flex items-center px-4 py-2 text-sm font-medium rounded-md no-underline
|
||||
${
|
||||
isActive
|
||||
${isActive
|
||||
? 'bg-primary-100 text-primary-900 dark:bg-primary-900 dark:text-primary-100'
|
||||
: 'text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700'
|
||||
}
|
||||
|
|
@ -112,8 +111,7 @@ export const DashboardLayout = ({ children }: DashboardLayoutProps) => {
|
|||
variant="neutral"
|
||||
className={`
|
||||
flex items-center px-4 py-2 text-sm font-medium rounded-md no-underline
|
||||
${
|
||||
isActive
|
||||
${isActive
|
||||
? 'bg-primary-100 text-primary-900 dark:bg-primary-900 dark:text-primary-100'
|
||||
: 'text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ export const MainLayout = ({ children }: MainLayoutProps) => {
|
|||
const router = useRouter()
|
||||
|
||||
const handleLogin = () => {
|
||||
router.push('/auth/login')
|
||||
router.push('/login')
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
23
web/components/ui/ErrorState.tsx
Normal file
23
web/components/ui/ErrorState.tsx
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import React from 'react'
|
||||
import { Button } from '../common/Button'
|
||||
import { ExclamationTriangleIcon } from '@heroicons/react/24/outline'
|
||||
|
||||
interface ErrorStateProps {
|
||||
message?: string
|
||||
onRetry?: () => void
|
||||
}
|
||||
|
||||
export function ErrorState({ message = 'Something went wrong.', onRetry }: ErrorStateProps) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center py-12 text-center">
|
||||
<ExclamationTriangleIcon className="h-12 w-12 text-red-500 mb-4" />
|
||||
<p className="text-gray-900 dark:text-white font-medium mb-2">Error</p>
|
||||
<p className="text-gray-500 dark:text-gray-400 mb-6 max-w-md">{message}</p>
|
||||
{onRetry && (
|
||||
<Button onClick={onRetry} variant="primary">
|
||||
Try Again
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
14
web/components/ui/LoadingState.tsx
Normal file
14
web/components/ui/LoadingState.tsx
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import React from 'react'
|
||||
|
||||
interface LoadingStateProps {
|
||||
message?: string
|
||||
}
|
||||
|
||||
export function LoadingState({ message = 'Loading...' }: LoadingStateProps) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center py-12">
|
||||
<div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-primary-600 mb-4"></div>
|
||||
<p className="text-gray-500 dark:text-gray-400">{message}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -63,7 +63,7 @@ apiClient.interceptors.response.use(
|
|||
localStorage.removeItem('refresh_token')
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
window.location.href = '/auth/login'
|
||||
window.location.href = '/login'
|
||||
}
|
||||
|
||||
return Promise.reject(refreshError)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
output: 'export',
|
||||
reactStrictMode: true,
|
||||
swcMinify: true,
|
||||
images: {
|
||||
unoptimized: true, // Required for static export
|
||||
remotePatterns: [
|
||||
{
|
||||
protocol: 'https',
|
||||
|
|
|
|||
10
web/package-lock.json
generated
10
web/package-lock.json
generated
|
|
@ -8,6 +8,7 @@
|
|||
"name": "morethanadiagnosis-web",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@heroicons/react": "^2.2.0",
|
||||
"@tanstack/react-query": "^5.25.0",
|
||||
"axios": "^1.6.0",
|
||||
"next": "^14.0.0",
|
||||
|
|
@ -163,6 +164,15 @@
|
|||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroicons/react": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.2.0.tgz",
|
||||
"integrity": "sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": ">= 16 || ^19.0.0-rc"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/config-array": {
|
||||
"version": "0.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
|
||||
|
|
|
|||
|
|
@ -12,14 +12,15 @@
|
|||
"format": "prettier --write \"**/*.{ts,tsx,md}\""
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"next": "^14.0.0",
|
||||
"@heroicons/react": "^2.2.0",
|
||||
"@tanstack/react-query": "^5.25.0",
|
||||
"axios": "^1.6.0",
|
||||
"zustand": "^4.4.0",
|
||||
"next": "^14.0.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"tailwindcss": "^3.3.0",
|
||||
"typescript": "^5.3.0"
|
||||
"typescript": "^5.3.0",
|
||||
"zustand": "^4.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.10.0",
|
||||
|
|
@ -27,10 +28,10 @@
|
|||
"@types/react-dom": "^18.2.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.13.0",
|
||||
"@typescript-eslint/parser": "^6.13.0",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"eslint": "^8.54.0",
|
||||
"eslint-config-next": "^14.0.0",
|
||||
"prettier": "^3.1.0",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"postcss": "^8.4.31"
|
||||
"postcss": "^8.4.31",
|
||||
"prettier": "^3.1.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
6
web/postcss.config.js
Normal file
6
web/postcss.config.js
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
|
|
@ -11,97 +11,64 @@ const config: Config = {
|
|||
extend: {
|
||||
colors: {
|
||||
primary: {
|
||||
DEFAULT: '#3B82F6',
|
||||
50: '#EFF6FF',
|
||||
100: '#DBEAFE',
|
||||
200: '#BFDBFE',
|
||||
300: '#93C5FD',
|
||||
400: '#60A5FA',
|
||||
500: '#3B82F6',
|
||||
600: '#2563EB',
|
||||
700: '#1D4ED8',
|
||||
800: '#1E40AF',
|
||||
900: '#1E3A8A',
|
||||
DEFAULT: '#0F4C5C', // Deep Teal (Authoritative)
|
||||
50: '#F0F6F7',
|
||||
100: '#E1EDF0',
|
||||
200: '#C3DBE1',
|
||||
300: '#A5C9D2',
|
||||
400: '#87B7C3',
|
||||
500: '#69A5B4',
|
||||
600: '#4B93A5',
|
||||
700: '#2D8196',
|
||||
800: '#0F4C5C', // Main
|
||||
900: '#0C3D4A',
|
||||
},
|
||||
secondary: {
|
||||
DEFAULT: '#8B5CF6',
|
||||
50: '#F5F3FF',
|
||||
100: '#EDE9FE',
|
||||
200: '#DDD6FE',
|
||||
300: '#C4B5FD',
|
||||
400: '#A78BFA',
|
||||
500: '#8B5CF6',
|
||||
600: '#7C3AED',
|
||||
700: '#6D28D9',
|
||||
800: '#5B21B6',
|
||||
900: '#4C1D95',
|
||||
DEFAULT: '#E6DACE', // Warm Sand (Tender)
|
||||
50: '#FCFBF9',
|
||||
100: '#F7F4F1',
|
||||
200: '#EFE9E3',
|
||||
300: '#E6DACE',
|
||||
400: '#D6C4B3',
|
||||
500: '#C6AE98',
|
||||
600: '#B6987D',
|
||||
700: '#A68262',
|
||||
800: '#8C6C4E',
|
||||
900: '#72563A',
|
||||
},
|
||||
background: {
|
||||
DEFAULT: '#F7F9F9', // Soft White (Professional)
|
||||
paper: '#FFFFFF',
|
||||
},
|
||||
text: {
|
||||
DEFAULT: '#334155', // Dark Slate (Readable)
|
||||
muted: '#64748B',
|
||||
},
|
||||
accent: {
|
||||
DEFAULT: '#E07A5F', // Soft Coral (Compassionate)
|
||||
},
|
||||
error: {
|
||||
DEFAULT: '#EF4444',
|
||||
50: '#FEF2F2',
|
||||
100: '#FEE2E2',
|
||||
200: '#FECACA',
|
||||
300: '#FCA5A5',
|
||||
400: '#F87171',
|
||||
500: '#EF4444',
|
||||
600: '#DC2626',
|
||||
700: '#B91C1C',
|
||||
800: '#991B1B',
|
||||
900: '#7F1D1D',
|
||||
},
|
||||
success: {
|
||||
DEFAULT: '#10B981',
|
||||
50: '#ECFDF5',
|
||||
100: '#D1FAE5',
|
||||
200: '#A7F3D0',
|
||||
300: '#6EE7B7',
|
||||
400: '#34D399',
|
||||
500: '#10B981',
|
||||
600: '#059669',
|
||||
700: '#047857',
|
||||
800: '#065F46',
|
||||
900: '#064E3B',
|
||||
},
|
||||
warning: {
|
||||
DEFAULT: '#F59E0B',
|
||||
50: '#FFFBEB',
|
||||
100: '#FEF3C7',
|
||||
200: '#FDE68A',
|
||||
300: '#FCD34D',
|
||||
400: '#FBBF24',
|
||||
500: '#F59E0B',
|
||||
600: '#D97706',
|
||||
700: '#B45309',
|
||||
800: '#92400E',
|
||||
900: '#78350F',
|
||||
},
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ['Inter', 'system-ui', 'sans-serif'],
|
||||
},
|
||||
fontSize: {
|
||||
xs: ['12px', { lineHeight: '1.5' }],
|
||||
sm: ['14px', { lineHeight: '1.5' }],
|
||||
base: ['16px', { lineHeight: '1.5' }],
|
||||
lg: ['18px', { lineHeight: '1.5' }],
|
||||
xl: ['20px', { lineHeight: '1.5' }],
|
||||
'2xl': ['24px', { lineHeight: '1.25' }],
|
||||
'3xl': ['32px', { lineHeight: '1.25' }],
|
||||
'4xl': ['48px', { lineHeight: '1.25' }],
|
||||
sans: ['var(--font-source-sans)', 'system-ui', 'sans-serif'],
|
||||
serif: ['var(--font-lora)', 'Georgia', 'serif'],
|
||||
heading: ['var(--font-lora)', 'Georgia', 'serif'],
|
||||
},
|
||||
borderRadius: {
|
||||
sm: '4px',
|
||||
DEFAULT: '8px',
|
||||
md: '8px',
|
||||
lg: '12px',
|
||||
sm: '8px',
|
||||
DEFAULT: '12px',
|
||||
md: '16px',
|
||||
lg: '24px',
|
||||
full: '9999px',
|
||||
},
|
||||
boxShadow: {
|
||||
sm: '0 1px 2px rgba(0, 0, 0, 0.05)',
|
||||
DEFAULT: '0 4px 6px rgba(0, 0, 0, 0.1)',
|
||||
md: '0 4px 6px rgba(0, 0, 0, 0.1)',
|
||||
lg: '0 10px 15px rgba(0, 0, 0, 0.15)',
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue