let junie rework the theme one more time
This commit is contained in:
@@ -3,7 +3,9 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>REMind</title>
|
<meta name="theme-color" content="#9d4eff" />
|
||||||
|
<meta name="description" content="REMind - Track and analyze your dreams with our dreamy app" />
|
||||||
|
<title>REMind | Dream Tracker</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
68
src/App.css
68
src/App.css
@@ -1,21 +1,24 @@
|
|||||||
#root {
|
#root {
|
||||||
max-width: 1280px;
|
width: 100%;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 2rem;
|
padding: 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.App {
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: var(--bg);
|
||||||
|
color: var(--text);
|
||||||
|
transition: background-color 0.3s, color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
height: 6em;
|
height: 6em;
|
||||||
padding: 1.5em;
|
|
||||||
will-change: filter;
|
will-change: filter;
|
||||||
transition: filter 300ms;
|
transition: filter 300ms;
|
||||||
}
|
}
|
||||||
.logo:hover {
|
.logo:hover {
|
||||||
filter: drop-shadow(0 0 2em #646cffaa);
|
filter: drop-shadow(0 0 2em var(--accent));
|
||||||
}
|
|
||||||
.logo.react:hover {
|
|
||||||
filter: drop-shadow(0 0 2em #61dafbaa);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes logo-spin {
|
@keyframes logo-spin {
|
||||||
@@ -34,9 +37,54 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
padding: 2em;
|
padding: 1.5em;
|
||||||
|
background-color: var(--card);
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 4px 12px var(--shadow);
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
transition: transform 0.2s, box-shadow 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.read-the-docs {
|
.card:hover {
|
||||||
color: #888;
|
transform: translateY(-3px);
|
||||||
|
box-shadow: 0 6px 16px var(--shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive adjustments */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.card {
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dreamy effects */
|
||||||
|
.dreamy-text {
|
||||||
|
background: var(--accent-gradient);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dreamy-border {
|
||||||
|
border: 2px solid transparent;
|
||||||
|
background:
|
||||||
|
linear-gradient(var(--bg), var(--bg)) padding-box,
|
||||||
|
var(--accent-gradient) border-box;
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dreamy-button {
|
||||||
|
background: var(--accent-gradient);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 0.6em 1.2em;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: transform 0.2s, box-shadow 0.2s;
|
||||||
|
box-shadow: 0 4px 12px var(--shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dreamy-button:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 6px 16px var(--shadow);
|
||||||
}
|
}
|
||||||
|
180
src/App.tsx
180
src/App.tsx
@@ -1,43 +1,175 @@
|
|||||||
import './App.css';
|
import './App.css';
|
||||||
|
import {useState} from 'react';
|
||||||
import Navbar from './components/Navbar';
|
import Navbar from './components/Navbar';
|
||||||
import TopBar from './components/TopBar';
|
import TopBar from './components/TopBar';
|
||||||
|
import SplashScreen from './components/SplashScreen';
|
||||||
import {BrowserRouter, Route, Routes} from 'react-router-dom';
|
import {BrowserRouter, Route, Routes} from 'react-router-dom';
|
||||||
import Feed from "./pages/Feed.tsx";
|
import Feed from "./pages/Feed.tsx";
|
||||||
import DreamPage from "./pages/DreamPage.tsx";
|
import DreamPage from "./pages/DreamPage.tsx";
|
||||||
|
import ProfilePage from "./pages/ProfilePage.tsx";
|
||||||
|
|
||||||
function Home() {
|
function Home() {
|
||||||
return <div className="p-4">Home Page</div>;
|
return (
|
||||||
|
<div className="p-4">
|
||||||
|
<h1 className="dreamy-text text-3xl md:text-4xl mb-6">Welcome to REMind</h1>
|
||||||
|
<p className="mb-8 text-lg">Your dreamy journey to better sleep and dream tracking</p>
|
||||||
|
|
||||||
|
<div className="dream-container">
|
||||||
|
<h2 className="dream-title">Track Your Dreams</h2>
|
||||||
|
<p className="mb-4">Record and analyze your dreams with our intuitive interface</p>
|
||||||
|
<button className="dreamy-button">Get Started</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mt-8">
|
||||||
|
<div className="card floating">
|
||||||
|
<h3 className="text-xl font-bold mb-2" style={{ color: 'var(--accent)' }}>Dream Journal</h3>
|
||||||
|
<p>Keep track of all your dreams in one place</p>
|
||||||
|
</div>
|
||||||
|
<div className="card" style={{ animationDelay: '2s' }}>
|
||||||
|
<h3 className="text-xl font-bold mb-2" style={{ color: 'var(--accent)' }}>Sleep Analysis</h3>
|
||||||
|
<p>Understand your sleep patterns better</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Record() {
|
function Record() {
|
||||||
return <div className="p-4">Record Page</div>;
|
return (
|
||||||
|
<div className="p-4">
|
||||||
|
<h1 className="dreamy-text text-3xl md:text-4xl mb-6">Record Your Dream</h1>
|
||||||
|
|
||||||
|
<div className="dream-container">
|
||||||
|
<div className="flex flex-col items-center mb-6">
|
||||||
|
<div className="w-20 h-20 rounded-full flex items-center justify-center mb-4 floating"
|
||||||
|
style={{ background: 'var(--accent-gradient)' }}>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" className="h-10 w-10 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<p className="text-lg mb-2">Tap to start recording your dream</p>
|
||||||
|
<p className="text-sm text-center" style={{ color: 'var(--text-muted)' }}>
|
||||||
|
Your voice will be transcribed automatically
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="dreamy-border p-4 mb-6">
|
||||||
|
<textarea
|
||||||
|
className="w-full p-3 rounded-lg bg-transparent focus:outline-none"
|
||||||
|
placeholder="Or type your dream here..."
|
||||||
|
rows={5}
|
||||||
|
style={{ color: 'var(--text)', borderColor: 'var(--accent-soft)' }}
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button className="dreamy-button w-full">Save Dream</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Archive() {
|
function Archive() {
|
||||||
return <div className="p-4">Archive Page</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Profile() {
|
|
||||||
return <div className="p-4">Profile Page</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function App() {
|
|
||||||
return (
|
return (
|
||||||
<BrowserRouter>
|
<div className="p-4">
|
||||||
<div className="pb-16 pt-8 min-h-screen">
|
<h1 className="dreamy-text text-3xl md:text-4xl mb-6">Dream Archive</h1>
|
||||||
<TopBar/>
|
|
||||||
<div className="mx-auto w-full max-w-lg">
|
<div className="mb-6 flex flex-col sm:flex-row gap-4">
|
||||||
<Navbar/>
|
<div className="flex-1">
|
||||||
<Routes>
|
<input
|
||||||
<Route path="/" element={<Home/>}/>
|
type="text"
|
||||||
<Route path="/feed" element={<Feed/>}/>
|
placeholder="Search dreams..."
|
||||||
<Route path="/record" element={<Record/>}/>
|
className="w-full p-3 rounded-lg focus:outline-none"
|
||||||
<Route path="/archive" element={<Archive/>}/>
|
style={{
|
||||||
<Route path="/profile" element={<Profile/>}/>
|
backgroundColor: 'var(--container)',
|
||||||
<Route path="/dream/:id" element={<DreamPage/>}/>
|
color: 'var(--text)',
|
||||||
</Routes>
|
borderColor: 'var(--accent-soft)',
|
||||||
|
boxShadow: '0 2px 8px var(--shadow)'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<select
|
||||||
|
className="w-full p-3 rounded-lg focus:outline-none"
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'var(--container)',
|
||||||
|
color: 'var(--text)',
|
||||||
|
borderColor: 'var(--accent-soft)',
|
||||||
|
boxShadow: '0 2px 8px var(--shadow)'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<option>All Dreams</option>
|
||||||
|
<option>Last Week</option>
|
||||||
|
<option>Last Month</option>
|
||||||
|
<option>Last Year</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</BrowserRouter>
|
|
||||||
|
<div className="space-y-4">
|
||||||
|
{[1, 2, 3].map((item) => (
|
||||||
|
<div key={item} className="card">
|
||||||
|
<div className="flex justify-between items-start mb-2">
|
||||||
|
<h3 className="text-xl font-bold" style={{ color: 'var(--accent)' }}>
|
||||||
|
Dream #{item}
|
||||||
|
</h3>
|
||||||
|
<span style={{ color: 'var(--text-muted)' }}>
|
||||||
|
{new Date().toLocaleDateString()}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<p className="mb-4">
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||||
|
Vivamus lacinia, nunc eu tincidunt faucibus...
|
||||||
|
</p>
|
||||||
|
<div className="flex justify-end">
|
||||||
|
<button
|
||||||
|
className="px-4 py-2 rounded-lg transition-transform hover:scale-105"
|
||||||
|
style={{
|
||||||
|
background: 'var(--accent-gradient)',
|
||||||
|
color: 'white'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
View Details
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default function App() {
|
||||||
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
|
||||||
|
const handleSplashFinished = () => {
|
||||||
|
setIsLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return <SplashScreen onFinished={handleSplashFinished}/>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="App">
|
||||||
|
<BrowserRouter>
|
||||||
|
<div className="pb-16 pt-8 min-h-screen">
|
||||||
|
<TopBar/>
|
||||||
|
<div className="mx-auto w-full max-w-lg px-4 sm:px-6 md:max-w-2xl lg:max-w-4xl">
|
||||||
|
<Navbar/>
|
||||||
|
<div className="mt-16 mb-20">
|
||||||
|
<Routes>
|
||||||
|
<Route path="/" element={<Home/>}/>
|
||||||
|
<Route path="/feed" element={<Feed/>}/>
|
||||||
|
<Route path="/record" element={<Record/>}/>
|
||||||
|
<Route path="/archive" element={<Archive/>}/>
|
||||||
|
<Route path="/profile" element={<ProfilePage/>}/>
|
||||||
|
<Route path="/dream/:id" element={<DreamPage/>}/>
|
||||||
|
</Routes>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</BrowserRouter>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
<svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
|
<svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
|
||||||
<defs>
|
<defs>
|
||||||
<linearGradient id="moonGradient" x1="0%" y1="0%" x2="100%" y2="50%">
|
<linearGradient id="moonGradient" x1="0%" y1="0%" x2="100%" y2="50%">
|
||||||
<stop offset="0%" stop-color="#5B68CE" />
|
<stop offset="0%" stop-color="darkorchid" />
|
||||||
<stop offset="100%" stop-color="#6A449E" />
|
<stop offset="100%" stop-color="darkslateblue" />
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
<mask id="moonMask">
|
<mask id="moonMask">
|
||||||
<circle cx="100" cy="100" r="50" fill="white" />
|
<circle cx="100" cy="100" r="50" fill="white" />
|
||||||
|
Before Width: | Height: | Size: 618 B After Width: | Height: | Size: 627 B |
@@ -4,8 +4,8 @@
|
|||||||
@import url('https://fonts.googleapis.com/css2?family=Raleway:wght@400;700&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=Raleway:wght@400;700&display=swap');
|
||||||
</style>
|
</style>
|
||||||
<linearGradient id="textGradient" x1="25%" y1="0%" x2="75%" y2="100%">
|
<linearGradient id="textGradient" x1="25%" y1="0%" x2="75%" y2="100%">
|
||||||
<stop offset="0%" stop-color="#5A8BFF" />
|
<stop offset="0%" stop-color="darkslateblue" />
|
||||||
<stop offset="100%" stop-color="#9B5CFF" />
|
<stop offset="100%" stop-color="indigo" />
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
</defs>
|
</defs>
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 715 B After Width: | Height: | Size: 720 B |
@@ -2,30 +2,31 @@ import {NavLink} from 'react-router-dom';
|
|||||||
import {FaArchive, FaHome, FaList, FaMicrophone, FaUser} from "react-icons/fa";
|
import {FaArchive, FaHome, FaList, FaMicrophone, FaUser} from "react-icons/fa";
|
||||||
|
|
||||||
export default function Navbar() {
|
export default function Navbar() {
|
||||||
|
|
||||||
return (<>
|
return (<>
|
||||||
<nav
|
<nav
|
||||||
className="fixed bottom-0 left-0 right-0 flex justify-around bg-violet-900 py-4 z-10">
|
className="fixed bottom-0 left-0 right-0 flex justify-around py-4 z-10"
|
||||||
<NavLink to="/">
|
style={{ backgroundColor: 'var(--accent-dark)', boxShadow: '0 -2px 10px var(--shadow)' }}>
|
||||||
<FaHome className="w-8 h-8 text-fuchsia-400"/>
|
<NavLink to="/" className="transition-transform hover:scale-110">
|
||||||
|
<FaHome className="w-6 h-6 md:w-8 md:h-8" style={{ color: 'var(--accent-soft)' }}/>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
<NavLink to="/feed">
|
<NavLink to="/feed" className="transition-transform hover:scale-110">
|
||||||
<FaList className="w-8 h-8 text-fuchsia-400"/>
|
<FaList className="w-6 h-6 md:w-8 md:h-8" style={{ color: 'var(--accent-soft)' }}/>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
<div className="w-20"></div>
|
<div className="w-16 md:w-20"></div>
|
||||||
<NavLink to="/archive">
|
<NavLink to="/archive" className="transition-transform hover:scale-110">
|
||||||
<FaArchive className="w-8 h-8 text-fuchsia-400"/>
|
<FaArchive className="w-6 h-6 md:w-8 md:h-8" style={{ color: 'var(--accent-soft)' }}/>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
<NavLink to="/profile">
|
<NavLink to="/profile" className="transition-transform hover:scale-110">
|
||||||
<FaUser className="w-8 h-8 text-fuchsia-400"/>
|
<FaUser className="w-6 h-6 md:w-8 md:h-8" style={{ color: 'var(--accent-soft)' }}/>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<NavLink
|
<NavLink
|
||||||
to="/record"
|
to="/record"
|
||||||
className="microphone-button fixed bottom-6 left-1/2 transform -translate-x-1/2 p-5 rounded-full z-20"
|
className="microphone-button fixed bottom-6 left-1/2 transform -translate-x-1/2 p-4 md:p-5 rounded-full z-20 transition-transform hover:scale-110"
|
||||||
|
style={{ boxShadow: '0 4px 15px var(--shadow)' }}
|
||||||
>
|
>
|
||||||
<FaMicrophone className="w-10 h-10 text-white"/>
|
<FaMicrophone className="w-8 h-8 md:w-10 md:h-10 text-white"/>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
31
src/components/SplashScreen.tsx
Normal file
31
src/components/SplashScreen.tsx
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
|
import logo from '../assets/logo.svg';
|
||||||
|
import text from '../assets/text.svg';
|
||||||
|
|
||||||
|
interface SplashScreenProps {
|
||||||
|
onFinished: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function SplashScreen({ onFinished }: SplashScreenProps) {
|
||||||
|
useEffect(() => {
|
||||||
|
// Simulate loading time with a timeout
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
onFinished();
|
||||||
|
}, 2000); // Show splash screen for 2 seconds
|
||||||
|
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
}, [onFinished]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 flex flex-col items-center justify-center bg-violet-900 z-50">
|
||||||
|
<div className="animate-pulse flex flex-col items-center">
|
||||||
|
<img src={logo} alt="REMind Logo" className="h-32 w-32 mb-4" />
|
||||||
|
<img src={text} alt="REMind Text" className="h-16" />
|
||||||
|
</div>
|
||||||
|
<div className="mt-8 text-fuchsia-400">
|
||||||
|
<div className="animate-spin h-8 w-8 border-4 border-current border-t-transparent rounded-full mx-auto"></div>
|
||||||
|
<p className="mt-4 text-center">Loading your dreams...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@@ -5,13 +5,41 @@ import text from '../assets/text.svg';
|
|||||||
export default function TopBar() {
|
export default function TopBar() {
|
||||||
const [visible, setVisible] = useState(true);
|
const [visible, setVisible] = useState(true);
|
||||||
const [lastScrollY, setLastScrollY] = useState(0);
|
const [lastScrollY, setLastScrollY] = useState(0);
|
||||||
|
const [darkMode, setDarkMode] = useState(false);
|
||||||
|
|
||||||
|
// Function to update theme meta tag
|
||||||
|
const updateThemeColor = (isDark: boolean) => {
|
||||||
|
const themeColor = isDark ? '#bb86fc' : '#9d4eff';
|
||||||
|
const metaThemeColor = document.querySelector('meta[name="theme-color"]');
|
||||||
|
if (metaThemeColor) {
|
||||||
|
metaThemeColor.setAttribute('content', themeColor);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialize theme based on user preference or system preference
|
||||||
|
useEffect(() => {
|
||||||
|
const isDarkMode = localStorage.getItem('darkMode') === 'true' ||
|
||||||
|
window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||||
|
setDarkMode(isDarkMode);
|
||||||
|
document.documentElement.setAttribute('data-theme', isDarkMode ? 'dark' : 'light');
|
||||||
|
updateThemeColor(isDarkMode);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Function to toggle theme
|
||||||
|
const toggleTheme = () => {
|
||||||
|
const newDarkMode = !darkMode;
|
||||||
|
setDarkMode(newDarkMode);
|
||||||
|
document.documentElement.setAttribute('data-theme', newDarkMode ? 'dark' : 'light');
|
||||||
|
localStorage.setItem('darkMode', newDarkMode.toString());
|
||||||
|
updateThemeColor(newDarkMode);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleScroll = () => {
|
const handleScroll = () => {
|
||||||
const currentScrollY = window.scrollY;
|
const currentScrollY = window.scrollY;
|
||||||
|
|
||||||
// Hide the topbar when scrolling down, show when scrolling up
|
// Hide the topbar when scrolling down, show when scrolling up
|
||||||
if (currentScrollY > lastScrollY) {
|
if (currentScrollY > lastScrollY && currentScrollY > 50) {
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
} else {
|
} else {
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
@@ -29,13 +57,39 @@ export default function TopBar() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`fixed top-0 left-0 right-0 bg-violet-900 py-3 px-4 flex items-center transition-transform duration-300 z-20 ${
|
className={`fixed top-0 left-0 right-0 py-2 md:py-3 px-3 md:px-4 flex items-center transition-transform duration-300 z-20 ${
|
||||||
visible ? 'transform-none' : 'transform -translate-y-full'
|
visible ? 'transform-none' : 'transform -translate-y-full'
|
||||||
}`}
|
}`}
|
||||||
|
style={{
|
||||||
|
background: 'var(--accent-gradient)',
|
||||||
|
boxShadow: '0 2px 10px var(--shadow)'
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center justify-between w-full max-w-6xl mx-auto">
|
||||||
<img src={logo} alt="REMind Logo" className="h-16 w-16" />
|
<div className="flex items-center">
|
||||||
<img src={text} alt="REMind Text" className="h-12 ml-2" />
|
<img src={logo} alt="REMind Logo" className="h-12 w-12 md:h-16 md:w-16" />
|
||||||
|
<img src={text} alt="REMind Text" className="h-8 md:h-12 ml-2" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={toggleTheme}
|
||||||
|
className="p-2 rounded-full focus:outline-none transition-transform hover:scale-110"
|
||||||
|
aria-label="Toggle theme"
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'var(--card)',
|
||||||
|
boxShadow: '0 2px 8px var(--shadow)'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{darkMode ? (
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5 md:h-6 md:w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" style={{ color: 'var(--accent)' }}>
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" />
|
||||||
|
</svg>
|
||||||
|
) : (
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5 md:h-6 md:w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" style={{ color: 'var(--accent-dark)' }}>
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" />
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@@ -1,11 +1,12 @@
|
|||||||
import User from "../types/User.ts";
|
import User from "../types/User.ts";
|
||||||
|
|
||||||
export const MockUsers: User[] = [
|
export const MockUsers: User[] = [
|
||||||
new User(1, "Matthias", "matthias.jpg"),
|
new User(1, "Matthias", "matthias.jpg", "s4mapuch@uni-trier.de", 42, 7),
|
||||||
new User(2, "Kim", "kim.png"),
|
new User(2, "Kim", "kim.png", "kim@example.com", 28, 3),
|
||||||
new User(3, "Anja", "anja.png"),
|
new User(3, "Anja", "anja.png", "anja@example.com", 15, 5),
|
||||||
|
new User(4, "Neo Quantum", "neo.png", "neo@remind.dev", 64, 12),
|
||||||
]
|
]
|
||||||
|
|
||||||
export const MockUserMap: Map<number, User> = new Map(MockUsers.map(user => [user.id, user]));
|
export const MockUserMap: Map<number, User> = new Map(MockUsers.map(user => [user.id, user]));
|
||||||
|
|
||||||
export default MockUsers;
|
export default MockUsers;
|
||||||
|
@@ -1,13 +1,16 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--bg: #f9f6ff;
|
--bg: #f8f5ff;
|
||||||
--container: #ede6fa;
|
--container: #ede6fa;
|
||||||
--card: #d6c6ff;
|
--card: #e0d4ff;
|
||||||
--text: #1e102e;
|
--text: #1e102e;
|
||||||
--text-muted: #4a375e;
|
--text-muted: #4a375e;
|
||||||
--accent: #7f39fb;
|
--accent: #9d4eff;
|
||||||
--accent-gradient: linear-gradient(135deg, #7f39fb, #d500f9);
|
--accent-gradient: linear-gradient(135deg, #9d4eff, #d500f9);
|
||||||
|
--accent-soft: #c9a4ff;
|
||||||
|
--accent-dark: #6a0dad;
|
||||||
|
--shadow: rgba(157, 78, 255, 0.2);
|
||||||
|
|
||||||
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
|
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
@@ -24,13 +27,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="dark"] {
|
[data-theme="dark"] {
|
||||||
--bg: #1e102e;
|
--bg: #1a0933;
|
||||||
--container: #3a1a5d;
|
--container: #2d1155;
|
||||||
--card: #5c2d91;
|
--card: #3d1a6e;
|
||||||
--text: #f5e6ff;
|
--text: #f5e6ff;
|
||||||
--text-muted: #c9a4e3;
|
--text-muted: #c9a4e3;
|
||||||
--accent: #bb86fc;
|
--accent: #bb86fc;
|
||||||
--accent-gradient: linear-gradient(135deg, #7f39fb, #d500f9);
|
--accent-gradient: linear-gradient(135deg, #bb86fc, #d500f9);
|
||||||
|
--accent-soft: #9d4eff;
|
||||||
|
--accent-dark: #6a0dad;
|
||||||
|
--shadow: rgba(187, 134, 252, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.border-background {
|
.border-background {
|
||||||
@@ -69,12 +75,13 @@ a:hover {
|
|||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
place-items: center;
|
|
||||||
min-width: 320px;
|
min-width: 320px;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
|
width: 100%;
|
||||||
background-color: var(--bg);
|
background-color: var(--bg);
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
transition: background-color 0.3s, color 0.3s;
|
transition: background-color 0.3s, color 0.3s;
|
||||||
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
@@ -173,3 +180,75 @@ button:focus-visible {
|
|||||||
.theme-toggle input:checked + .slider {
|
.theme-toggle input:checked + .slider {
|
||||||
background-color: var(--accent);
|
background-color: var(--accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Dreamy violet enhancements */
|
||||||
|
.dream-title {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
background: var(--accent-gradient);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dream-container {
|
||||||
|
background-color: var(--container);
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 1.5rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
box-shadow: 0 4px 15px var(--shadow);
|
||||||
|
transition: transform 0.3s, box-shadow 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dream-container:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: 0 8px 25px var(--shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scrollbar styling */
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: var(--bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--accent-soft);
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animations */
|
||||||
|
@keyframes dream-float {
|
||||||
|
0% {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: translateY(-10px);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating {
|
||||||
|
animation: dream-float 6s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive typography */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
h1 {
|
||||||
|
font-size: 2.5em;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-size: 1.8em;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
font-size: 0.95em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
67
src/pages/ProfilePage.tsx
Normal file
67
src/pages/ProfilePage.tsx
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { MockUsers } from '../data/MockUsers';
|
||||||
|
|
||||||
|
const ProfilePage: React.FC = () => {
|
||||||
|
// Find Neo Quantum in MockUsers
|
||||||
|
const profileUser = MockUsers.find(user => user.name === "Neo Quantum");
|
||||||
|
|
||||||
|
// Default values if user not found
|
||||||
|
const defaultName = "TestUser";
|
||||||
|
const defaultEmail = "user@example.com";
|
||||||
|
const defaultDreamCount = 0;
|
||||||
|
const defaultStreakDays = 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="page p-4">
|
||||||
|
<div className="dreamPanel flex flex-col items-center p-6">
|
||||||
|
{/* Profile Picture */}
|
||||||
|
<div className="w-32 h-32 rounded-full overflow-hidden mb-4 border-4 border-accent">
|
||||||
|
<img
|
||||||
|
src={profileUser ? `/assets/profiles/${profileUser.profilePicture}` : `https://ui-avatars.com/api/?name=${defaultName}&background=random&color=fff&size=128`}
|
||||||
|
alt={profileUser ? profileUser.name : defaultName}
|
||||||
|
className="w-full h-full object-cover"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* User Information */}
|
||||||
|
<div className="text-center">
|
||||||
|
<h2 className="text-xl font-bold mb-2">{profileUser ? profileUser.name : defaultName}</h2>
|
||||||
|
<p className="text-text-muted mb-4">{profileUser ? profileUser.email : defaultEmail}</p>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-2 gap-4 mt-6 text-center">
|
||||||
|
<div className="dreamPanel">
|
||||||
|
<p className="font-bold text-2xl">{profileUser ? profileUser.dreamCount : defaultDreamCount}</p>
|
||||||
|
<p className="text-sm text-text-muted">Dreams</p>
|
||||||
|
</div>
|
||||||
|
<div className="dreamPanel">
|
||||||
|
<p className="font-bold text-2xl">{profileUser ? profileUser.streakDays : defaultStreakDays}</p>
|
||||||
|
<p className="text-sm text-text-muted">Days Streak</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-8">
|
||||||
|
<h3 className="text-lg font-semibold mb-2">Account Settings</h3>
|
||||||
|
<div className="dreamPanel text-left">
|
||||||
|
<div className="flex justify-between items-center py-2">
|
||||||
|
<span>Notifications</span>
|
||||||
|
<label className="theme-toggle">
|
||||||
|
<input type="checkbox" defaultChecked />
|
||||||
|
<span className="slider"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between items-center py-2">
|
||||||
|
<span>Privacy</span>
|
||||||
|
<label className="theme-toggle">
|
||||||
|
<input type="checkbox" />
|
||||||
|
<span className="slider"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ProfilePage;
|
@@ -2,14 +2,23 @@ export default class User {
|
|||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
profilePicture: string;
|
profilePicture: string;
|
||||||
|
email: string;
|
||||||
|
dreamCount: number;
|
||||||
|
streakDays: number;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
id: number,
|
id: number,
|
||||||
name: string,
|
name: string,
|
||||||
profilePicture: string
|
profilePicture: string,
|
||||||
|
email: string = "",
|
||||||
|
dreamCount: number = 0,
|
||||||
|
streakDays: number = 0
|
||||||
){
|
){
|
||||||
this.id = id
|
this.id = id
|
||||||
this.name = name
|
this.name = name
|
||||||
this.profilePicture = profilePicture
|
this.profilePicture = profilePicture
|
||||||
|
this.email = email
|
||||||
|
this.dreamCount = dreamCount
|
||||||
|
this.streakDays = streakDays
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user