Compare commits

...

31 Commits

Author SHA1 Message Date
4ccbd28c4f expanded and updated README.md to include detailed project overview, features breakdown, technical stack, usage guide, project structure, and educational context for improved clarity and documentation quality.
Signed-off-by: Matthias Puchstein <matthias@puchstein.bayern>
2025-07-17 04:18:19 +02:00
618e5b442e refactored DreamCharts to consolidate chart rendering logic into reusable renderChart function; improved chart resize handling for better responsiveness and maintainability
Signed-off-by: Matthias Puchstein <matthias@puchstein.bayern>
2025-07-17 03:56:10 +02:00
936a2fa1ee adjusted chip icon position in DreamCardCompact to align with design improvements
Signed-off-by: Matthias Puchstein <matthias@puchstein.bayern>
2025-07-17 03:50:53 +02:00
2165a63bfb added @types/node and dependencies to devDependencies for extended Node.js type support; adjusted chip icon positioning in DreamCardCompact for improved UI alignment
Signed-off-by: Matthias Puchstein <matthias@puchstein.bayern>
2025-07-17 03:50:18 +02:00
2d294de69a refactored DreamVR to simplify component structure, integrate OrbitControls for non-VR mode, enhance lighting, and optimize render logic for improved performance and maintainability
Signed-off-by: Matthias Puchstein <matthias@puchstein.bayern>
2025-07-17 03:36:20 +02:00
7754370a70 swapped colors and labels for "Sexuelle Träume" and "Flugträume" for consistent data representation and accuracy in Technology visualization
Signed-off-by: Matthias Puchstein <matthias@puchstein.bayern>
2025-07-17 03:34:06 +02:00
2edf6b3f1f introduced WebXR support by integrating webxr-polyfill and refactoring DreamVR component for enhanced VR functionality and simplified code structure
Signed-off-by: Matthias Puchstein <matthias@puchstein.bayern>
2025-07-17 03:22:19 +02:00
8b0996781e removed unused logo from README.md for cleaner presentation
Signed-off-by: Matthias Puchstein <matthias@puchstein.bayern>
2025-07-17 03:04:20 +02:00
bb714e6fa2 refined ProfilePage UI by improving spacing, aligning component structure, and reintroducing project information card for enriched profile context and presentation
Signed-off-by: Matthias Puchstein <matthias@puchstein.bayern>
2025-07-17 02:45:12 +02:00
75f516b408 enabled looping for dream videos on DreamPage for seamless playback experience
Signed-off-by: Matthias Puchstein <matthias@puchstein.bayern>
2025-07-17 02:34:26 +02:00
8403a17636 updated package-lock.json to add and lock dependencies for extended multimedia, VR, and UI support, including @react-three/xr, @fortawesome, zustand, and others for enhanced feature integration and compatibility
Signed-off-by: Matthias Puchstein <matthias@puchstein.bayern>
2025-07-17 02:29:41 +02:00
44353988a1 updated description field in MockDreams dataset for concise and refined narrative of digital garden dream visuals
Signed-off-by: Matthias Puchstein <matthias@puchstein.bayern>
2025-07-17 02:28:02 +02:00
1d3d684c8f added favicon.ico to public directory for browser tab icon display
Signed-off-by: Matthias Puchstein <matthias@puchstein.bayern>
2025-07-17 01:44:05 +02:00
86e145515e added chip icon to DreamCard and DreamCardCompact for visual representation of chip input type
Signed-off-by: Matthias Puchstein <matthias@puchstein.bayern>
2025-07-17 01:29:35 +02:00
fca8d962f4 updated image file reference in MockDreams dataset (img field) for improved consistency with naming conventions
Signed-off-by: Matthias Puchstein <matthias@puchstein.bayern>
2025-07-17 01:10:07 +02:00
19cc813de7 expanded multimedia support in MockDreams by adding audio, image, and video fields to enhance dream representation
Signed-off-by: Matthias Puchstein <matthias@puchstein.bayern>
2025-07-17 01:02:40 +02:00
2579460c1f refactored DreamPage to use lazy loading with Suspense for charts and media, modularized chart components into DreamCharts.tsx using d3, and optimized build by grouping libraries into separate chunks in vite.config.ts
Signed-off-by: Matthias Puchstein <matthias@puchstein.bayern>
2025-07-17 00:53:56 +02:00
afec14c390 added DreamVR component for neural network-based VR visualization of chip input dreams, integrated @react-three/fiber and @react-three/drei dependencies, and updated package-lock.json
Signed-off-by: Matthias Puchstein <matthias@puchstein.bayern>
2025-07-17 00:19:41 +02:00
c4bf7c8b20 added audio field enhancements in MockDreams, improved chart handling for chip inputs (EEG, vitals, and movement visualization) using d3, and expanded multimedia support in DreamPage UI components
Signed-off-by: Matthias Puchstein <matthias@puchstein.bayern>
2025-07-17 00:01:44 +02:00
0c3b2bf81d updated chip input type in MockDreams: replaced description with text, adjusted Dream type, and extended UI components for consistent rendering across DreamPage and DreamCard
Signed-off-by: Matthias Puchstein <matthias@puchstein.bayern>
2025-07-16 23:13:00 +02:00
881f832722 added chip input type to MockDreams dataset, including support for EEG, pulse, HRV, movement data, and updated Dream type for enhanced physiological dream representation
Signed-off-by: Matthias Puchstein <matthias@puchstein.bayern>
2025-07-16 22:23:43 +02:00
73e022212f added d3 and @types/d3 dependencies for enhanced data visualization capabilities in UI development
Signed-off-by: Matthias Puchstein <matthias@puchstein.bayern>
2025-07-16 22:11:57 +02:00
af27b23c2f updated MockDreams dataset to support audio input type, added transcript and audio fields for enhanced multimedia representation of dreams
Signed-off-by: Matthias Puchstein <matthias@puchstein.bayern>
2025-07-16 21:21:01 +02:00
d38947d316 added dynamic tags display to DreamPage for improved visualization and categorization of dream data
Signed-off-by: Matthias Puchstein <matthias@puchstein.bayern>
2025-07-16 21:15:52 +02:00
33f68a7e49 replaced single dream type tag with dynamic tags display in DreamCard for improved categorization and flexibility in UI rendering
Signed-off-by: Matthias Puchstein <matthias@puchstein.bayern>
2025-07-16 21:13:08 +02:00
ecd3fa120d added tags field to MockDreams dataset for categorization of dreams, improving clarity and enabling better filtering options across components
Signed-off-by: Matthias Puchstein <matthias@puchstein.bayern>
2025-07-16 21:10:28 +02:00
38d01821b2 enhanced ResearchLive project card layout: added min-height and flex properties to improve aesthetic and text alignment consistency
Signed-off-by: Matthias Puchstein <matthias@puchstein.bayern>
2025-07-16 20:58:05 +02:00
6471ffb916 enhanced ResearchLive project card layout: added min-height and flex properties to improve aesthetic and text alignment consistency
Signed-off-by: Matthias Puchstein <matthias@puchstein.bayern>
2025-07-16 20:53:05 +02:00
6e1de4b48f refactored ResearchLive data structure and components: updated mock data models, removed researcher interviews section, and revised StudyCard handling for optional participants field
Signed-off-by: Matthias Puchstein <matthias@puchstein.bayern>
2025-07-16 20:47:42 +02:00
20dfb50ac8 updated MockDreams initial record to use a fixed date for consistent test results and predictable behavior in data processing
Signed-off-by: Matthias Puchstein <matthias@puchstein.bayern>
2025-07-16 20:17:00 +02:00
b8e8057442 removed redundant media references from MockDreams data; updated DreamPage carousel container class for specificity and styled controls in App.css for improved UI consistencies
Signed-off-by: Matthias Puchstein <matthias@puchstein.bayern>
2025-07-16 20:13:09 +02:00
19 changed files with 3765 additions and 558 deletions

311
README.md
View File

@@ -1,92 +1,269 @@
<img src="https://r2cdn.perplexity.ai/pplx-full-logo-primary-dark%402x.png" class="logo" width="120"/>
# REMind - Dream Analysis & VR Experience Platform
# REMind (Mockup Demo)
![Project Status](https://img.shields.io/badge/Status-University%20Project%20Demo-orange)
![License](https://img.shields.io/badge/License-AGPL--3.0-blue)
![TypeScript](https://img.shields.io/badge/TypeScript-95.2%25-blue)
🚧 **This is a MOCKUP / DEMO APPLICATION for a university project no real backend, AI features, or data storage.** 🚧
> **🚧 IMPORTANT: This is a MOCKUP/DEMO APPLICATION for a university project**
> **No real backend, AI features, or data storage. All functionality is simulated for demonstration purposes.**
REMind is an interactive, front-end prototype that showcases dream journaling, AI-powered analysis (simulated), and immersive VR demonstrations. Built with React, TypeScript, Vite, and Tailwind CSS, its designed for demos and concept validation only.
## Project Overview
## Features (all mockup/demo only)
REMind is an innovative interactive prototype that explores the intersection of dream analysis, artificial intelligence,
and virtual reality. The application demonstrates how modern technology could revolutionize dream journaling and
interpretation through immersive experiences and intelligent analysis.
- **(Mockup)** Dream Journal
Record dreams as text entries, voice memos, or sketches—UI only, no persistence.
- **(Mockup)** AI Analysis
Simulated “Here the AI would analyze your dream…” step with instant, hard-coded interpretations, symbol recognition, and emotion tagging.
- **(Mockup)** Personal Archive
Browse and filter past mock entries by date, symbols, emotions, or tags.
- **(Mockup)** Statistics \& Trends
Visualize frequent symbols, emotional patterns, and simulated sleep-cycle correlations using dummy data.
- **(Mockup)** VR Dreamscapes
Enter 3D scenes built with React Three Fiber—explore thematic environments (labyrinths, dark rooms) in browser-based VR mode.
- **Responsive Design**
Works on desktop browsers(design optimized for mobile) and mobile devices. Ideal for QR-code launches at demos.
**Project Aim:** To showcase the potential of combining dream journaling with AI-powered analysis and VR visualization,
creating an engaging platform for understanding the subconscious mind and exploring the societal implications of dream
research.
## Tech Stack
## ✨ Key Features
- **Framework:** React + TypeScript
- **Bundler:** Vite
- **Styling:** Tailwind CSS
- **Routing:** React Router Dom
- **Icons:** React Icons
- **3D \& VR:** @react-three/fiber
- **Linting:** ESLint
### 🌙 Multi-Modal Dream Recording
## Getting Started
- **Text Input**: Write detailed dream descriptions with rich formatting
- **Voice Recording**: Record dream narratives with audio transcription
- **Image Upload**: Upload dream sketches, drawings, or related images
- **Biometric Integration**: Simulate EEG, heart rate, and movement data from sleep tracking devices
> **Note:** This repository contains only a front-end prototype with hard-coded mock data. There is no production backend or AI service.
### 🤖 AI-Powered Analysis (Simulated)
1. Clone the repo
- **Symbol Recognition**: Identify and categorize dream symbols
- **Emotion Detection**: Analyze emotional patterns and mood indicators
- **Interpretation Engine**: Generate personalized dream interpretations
- **Pattern Analysis**: Detect recurring themes and symbols across dreams
```bash
git clone https://gitea.puchstein.bayern/mpuchstein/REMind.git
cd REMind
```
### 🎮 VR Dreamscapes
2. Install dependencies
- **3D Visualization**: Transform dreams into immersive 3D environments
- **Neural Network Visualization**: Interactive neural network representations for biometric data
- **VR Experience**: WebXR-enabled virtual reality exploration
- **Responsive Controls**: Support for both VR headsets and desktop interaction
```bash
npm install
```
### 📊 Advanced Analytics
3. Run development server
- **EEG Visualization**: Real-time brainwave pattern charts (Alpha, Beta, Theta, Delta)
- **Vital Signs Monitoring**: Heart rate and HRV trend analysis
- **Movement Tracking**: Sleep movement pattern visualization
- **Statistical Dashboard**: Comprehensive dream pattern analysis
```bash
npm run dev
```
### 🗂️ Dream Archive & Management
Open `http://localhost:5173` in your browser. 4. Build for production (static demo)
- **Personal Library**: Organized collection of all dream entries
- **Search & Filter**: Advanced filtering by date, symbols, emotions, and tags
- **Social Features**: Share dreams with friends and community
- **Daily Highlights**: Curated dream insights and patterns
```bash
npm run build
npm run preview
```
### 🌐 Community Features
## Project Structure
- **Dream Archive**: Explore categorized dream collections
- **Research Integration**: Live research data visualization
- **Cultural Landscapes**: Dreams categorized by cultural themes
- **Worldwide Events**: Global dream pattern analysis
## 🛠️ Technology Stack
### Frontend Framework
- **React 19.1.0** - Modern React with latest features
- **TypeScript** - Type-safe development
- **Vite 7.0.0** - Fast build tool and development server
### Styling & UI
- **Tailwind CSS 4.1.11** - Utility-first CSS framework
- **CSS Variables** - Dynamic theming system
- **Responsive Design** - Mobile-first approach
### 3D & VR
- **Three.js 0.178.0** - 3D graphics library
- **@react-three/fiber 9.2.0** - React Three.js renderer
- **@react-three/drei 10.5.1** - Three.js helpers
- **@react-three/xr 6.6.19** - WebXR integration
- **webxr-polyfill 2.0.3** - VR compatibility
### Data Visualization
- **D3.js 7.9.0** - Advanced data visualization
- **Custom Chart Components** - EEG, vitals, and movement charts
### Navigation & Routing
- **React Router DOM 7.6.3** - Client-side routing
- **React Icons 5.5.0** - Comprehensive icon library
### Development Tools
- **ESLint** - Code linting and formatting
- **TypeScript ESLint** - TypeScript-specific linting
- **Vite Plugin React** - React integration
## 📁 Project Structure
```
.
├── public/ # Static assets
REMind/
├── public/ # Static assets (favicon, etc.)
├── src/
│ ├── assets/ # Images, fonts
│ ├── components/ # Reusable React components
│ ├── data/ # Mock data (dream entries, analysis)
│ ├── pages/ # Route-driven pages
│ ├── types/ # TypeScript types/interfaces
│ ├── App.tsx # Root component
│ ├── index.css # Tailwind setup
│ └── main.tsx # Entry point
├── index.html # HTML template
├── package.json # Scripts & dependencies
├── tsconfig.app.json # TS config for app
├── vite.config.ts # Vite config
└── README.md # This file
│ ├── assets/ # Images, logos, fonts
│ ├── components/ # Reusable UI components
│ ├── DreamCard.tsx # Dream display component
│ ├── DreamRecord.tsx # Dream recording interface
│ ├── DreamVR.tsx # VR visualization component
│ ├── DreamCharts.tsx # Data visualization components
│ ├── Navbar.tsx # Navigation component
│ └── ...
│ ├── data/ # Mock data for demonstration
│ │ ├── MockDreams.ts # Sample dream entries
│ │ ├── MockUsers.ts # User data
└── ...
│ ├── pages/ # Route-driven page components
│ │ ├── Home.tsx # Main dashboard
│ │ ├── Feed.tsx # Dream feed
│ │ ├── DreamPage.tsx # Individual dream view
│ │ ├── Overview.tsx # Landing page
│ │ └── dreamarchive/ # Archive pages
│ ├── types/ # TypeScript type definitions
│ │ ├── Dream.ts # Dream data structures
│ │ └── User.ts # User data structures
│ ├── utils/ # Utility functions
│ ├── styles/ # Styling utilities
│ ├── App.tsx # Root component
│ ├── main.tsx # Application entry point
│ └── index.css # Global styles
├── docs/ # Documentation
│ └── poster.pdf # Project presentation poster
├── package.json # Dependencies and scripts
├── tsconfig.app.json # TypeScript configuration
├── vite.config.ts # Vite configuration
└── README.md # This file
```
## Usage \& Demo
## 🚀 Getting Started
- **Start Dream Journal** — click “🌙 Lass die Magie beginnen” to open the mock dream entry form.
- **AI Analysis** — submit an entry to see the “mock” interpretation card.
- **Browse Archive** — filter mock entries by symbol, emotion, or date.
- **VR Mode** — click VR Demo to load an immersive 3D scene in your browser.
### Prerequisites
**Reminder:** REMind is a design-and-concept prototype only. Enjoy exploring the features—no real data or AI integration under the hood!
- Node.js 18+
- npm or yarn package manager
### Installation
1. **Clone the repository**
```bash
git clone https://gitea.puchstein.bayern/mpuchstein/REMind.git
cd REMind
```
2. **Install dependencies**
```bash
npm install
```
3. **Start development server**
```bash
npm run dev
```
4. **Open in browser**
```
http://localhost:5173
```
### Available Scripts
- `npm run dev` - Start development server
- `npm run build` - Build for production
- `npm run preview` - Preview production build
- `npm run lint` - Run ESLint
## 🎯 Usage Guide
### Getting Started
1. **Landing Page**: Visit the overview page to understand the application concept
2. **Begin Journey**: Click "Lass die Magie beginnen" to start the dream recording experience
3. **Record Dreams**: Use various input methods (text, voice, image, biometric)
4. **AI Analysis**: View simulated AI interpretations and insights
5. **VR Experience**: Explore dreams in virtual reality environments
6. **Archive Exploration**: Browse and filter through dream collections
### Key Navigation
- **Home** (`/home`) - Personal dashboard with recent dreams
- **Feed** (`/feed`) - Community dream sharing
- **Record** (`/record`) - Dream input interface
- **Dream Archive** (`/dreamarchive`) - Categorized dream collections
- **Profile** (`/profile`) - User settings and statistics
### Input Types
- **Text**: Write detailed dream descriptions
- **Audio**: Record voice memos with transcription
- **Image**: Upload dream-related visuals
- **Chip**: Biometric data from sleep tracking devices
## 🔮 Demo Features
### Simulated AI Analysis
- **Symbol Recognition**: Identifies dream symbols like "water," "flying," "animals"
- **Emotion Tagging**: Categorizes emotions as "fear," "joy," "anxiety," "peace"
- **Interpretation**: Generates psychological insights and meanings
- **Pattern Detection**: Highlights recurring themes across dreams
### VR Visualization
- **Neural Networks**: Interactive 3D representations of brain activity
- **Dreamscapes**: Immersive environments based on dream content
- **Data Visualization**: 3D charts and graphs for biometric data
- **WebXR Support**: Compatible with VR headsets and desktop browsers
### Data Analytics
- **EEG Patterns**: Brainwave analysis during sleep (Alpha, Beta, Theta, Delta)
- **Vital Signs**: Heart rate and heart rate variability trends
- **Movement Data**: Sleep movement and restlessness patterns
- **Statistical Insights**: Long-term pattern analysis and correlations
## ⚠️ Important Notes
### Assets Not Included
- **Media Files**: Dream images, audio files, and videos are not included in the repository
- **Mock Data**: All displayed content uses placeholder data for demonstration
- **External Resources**: Some images and media are referenced but not stored in the repository
### Limitations
- **No Backend**: All data is stored locally in browser memory
- **No AI Integration**: Analysis results are pre-generated mock responses
- **No Data Persistence**: Data is lost when browser is refreshed
- **Demo Purpose Only**: Not intended for production use
## 🎓 Educational Context
This project was developed as a university demonstration to explore:
- **Human-Computer Interaction**: Innovative interfaces for dream recording
- **Data Visualization**: Advanced techniques for complex data representation
- **Virtual Reality**: Immersive experiences for psychological content
- **AI Integration**: Simulated machine learning for content analysis
- **User Experience**: Intuitive design for sensitive personal data
## 🤝 Contributing
This is a university project demo and not actively maintained for production use. However, feedback and suggestions are
welcome for educational purposes.
## 📄 License
This project is licensed under the AGPL-3.0 License - see the LICENSE file for details.
## 🔗 Repository
[View on Gitea](https://gitea.puchstein.bayern/mpuchstein/REMind)
---
**Disclaimer**: REMind is a prototype demonstration created for educational purposes. All AI features, data analysis,
and interpretations are simulated and should not be used for actual psychological or medical analysis.

BIN
docs/poster.pdf Normal file

Binary file not shown.

1981
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,20 +10,30 @@
"preview": "vite preview"
},
"dependencies": {
"@react-three/drei": "^10.5.1",
"@react-three/fiber": "^9.2.0",
"@react-three/xr": "^6.6.19",
"@tailwindcss/vite": "^4.1.11",
"d3": "^7.9.0",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-icons": "^5.5.0",
"react-router-dom": "^7.6.3",
"react-slick": "^0.30.3",
"slick-carousel": "^1.8.1",
"tailwindcss": "^4.1.11"
"tailwindcss": "^4.1.11",
"three": "^0.178.0",
"webxr-polyfill": "^2.0.3"
},
"devDependencies": {
"@eslint/js": "^9.29.0",
"@types/d3": "^7.4.3",
"@types/node": "^24.0.14",
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",
"@types/react-slick": "^0.23.13",
"@types/three": "^0.178.1",
"@types/webxr": "^0.5.22",
"@vitejs/plugin-react": "^4.5.2",
"eslint": "^9.29.0",
"eslint-plugin-react-hooks": "^5.2.0",

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -97,3 +97,37 @@
box-shadow: 0 15px 35px var(--shadow), inset 0 0 20px rgba(255, 255, 255, 0.3);
opacity: 1;
}
/* Carousel controls styling */
.carousel-container .slick-prev,
.carousel-container .slick-next {
z-index: 10;
width: 40px;
height: 40px;
background: rgba(166, 77, 255, 0.7);
border-radius: 50%;
display: flex !important;
align-items: center;
justify-content: center;
padding: 10px;
transition: all 0.3s ease;
}
.carousel-container .slick-prev {
left: 20px;
}
.carousel-container .slick-next {
right: 20px;
}
.carousel-container .slick-prev:hover,
.carousel-container .slick-next:hover {
background: rgba(166, 77, 255, 0.9);
}
.carousel-container .slick-prev:before,
.carousel-container .slick-next:before {
font-size: 20px;
opacity: 1;
}

View File

@@ -5,6 +5,7 @@ import {NavLink} from "react-router-dom";
import {useEffect, useState} from "react";
import {getTextStyle} from '../styles/StyleUtils';
import {formatDateWithTime} from '../utils/DateUtils';
import {FaMicrochip} from 'react-icons/fa6';
interface DreamCardProps {
dream: Dream;
@@ -24,11 +25,17 @@ export default function DreamCard({dream, user, animationDelay = 0}: DreamCardPr
}, [animationDelay]);
return (<NavLink key={dream.id} to={`/dream/${dream.id}`}>
<li className={`dreamy-card backdrop-blur-md mb-4 transition-all duration-500 ${isVisible ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-4'}`}
<li className={`dreamy-card backdrop-blur-md mb-4 transition-all duration-500 ${isVisible ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-4'} relative`}
style={{
background: 'var(--purple-gradient)',
animationDelay: `${animationDelay * 0.2}s`
}}>
{dream.input.inputType === 'chip' && (
<div className="absolute top-6 right-10 w-10 h-10 flex items-center justify-center rounded-full"
style={{background: 'var(--accent-gradient)', boxShadow: '0 0 5px var(--shadow)'}}>
<FaMicrochip className="w-6 h-6 md:w-8 md:h-8 text-pink-300 opacity-90 hover:opacity-100"/>
</div>
)}
<div className="flex rounded items-center mb-4">
<div className="relative">
<img
@@ -48,22 +55,25 @@ export default function DreamCard({dream, user, animationDelay = 0}: DreamCardPr
<p className="mt-2 line-clamp-2 text-left" style={getTextStyle()}>
{(dream.input.inputType === 'text' && dream.input.input)
|| (dream.input.inputType === 'audio' && dream.input.transcript)
|| (dream.input.inputType === 'image' && dream.input.description)}
|| (dream.input.inputType === 'image' && dream.input.description)
|| (dream.input.inputType === 'chip' && dream.input.text)}
</p>
<div className="flex justify-between items-center mt-4">
<p className="timestamp text-left" style={getTextStyle('muted')}>
{formatDateWithTime(dream.date)}
</p>
<div className="flex space-x-2">
{/* Display dream type as a tag */}
<span className="px-2 py-1 text-xs rounded-full"
style={{
background: 'var(--accent-gradient)',
color: 'white',
boxShadow: '0 2px 5px var(--shadow)'
}}>
{dream.input.inputType}
</span>
{/* Display dream tags */}
{dream.tags.map((tag, index) => (
<span key={index} className="px-2 py-1 text-xs rounded-full"
style={{
background: 'var(--accent-gradient)',
color: 'white',
boxShadow: '0 2px 5px var(--shadow)'
}}>
{tag}
</span>
))}
</div>
</div>
</li>

View File

@@ -2,6 +2,7 @@ import {NavLink} from 'react-router-dom';
import {formatDateSimple} from '../utils/DateUtils';
import Dream from '../types/Dream';
import User from '../types/User';
import {FaMicrochip} from 'react-icons/fa6';
interface DreamCardCompactProps {
dream: Dream;
@@ -22,6 +23,12 @@ export const DreamCardCompact = ({dream, index, showUser = false, user}: DreamCa
boxShadow: '0 8px 20px var(--shadow), inset 0 0 10px rgba(166, 77, 255, 0.2)',
border: '1px solid rgba(166, 77, 255, 0.1)'
}}>
{dream.input.inputType === 'chip' && (
<div className="absolute bottom-2 right-2 w-10 h-10 flex items-center justify-center rounded-full"
style={{background: 'var(--accent-gradient)', boxShadow: '0 0 5px var(--shadow)'}}>
<FaMicrochip className="w-6 h-6 md:w-8 md:h-8 text-pink-300 opacity-90 hover:opacity-100"/>
</div>
)}
<div className="flex flex-col mb-1">
{showUser && user && (
<div className="flex justify-center mb-2">

View File

@@ -0,0 +1,350 @@
import React, {useEffect, useRef} from 'react';
import * as d3 from 'd3';
import type {ChipInput} from '../types/Dream';
// EEG Chart Component
export const EEGChart: React.FC<{ chipInput: ChipInput }> = ({chipInput}) => {
const chartRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!chartRef.current) return;
const eegData = [
{name: 'Alpha', values: chipInput.eeg.alpha},
{name: 'Beta', values: chipInput.eeg.beta},
{name: 'Theta', values: chipInput.eeg.theta},
{name: 'Delta', values: chipInput.eeg.delta}
];
// Function to render the chart
const renderChart = () => {
if (!chartRef.current) return;
// Clear previous chart
d3.select(chartRef.current).selectAll('*').remove();
const margin = {top: 20, right: 20, bottom: 30, left: 50};
const width = chartRef.current.clientWidth - margin.left - margin.right;
const height = chartRef.current.clientHeight - margin.top - margin.bottom;
const svg = d3.select(chartRef.current)
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
// X scale
const x = d3.scaleLinear()
.domain([0, chipInput.eeg.alpha.length - 1])
.range([0, width]);
// Y scale
const y = d3.scaleLinear()
.domain([0, d3.max(eegData.flatMap(d => d.values)) || 50])
.range([height, 0]);
// Line generator
const line = d3.line<number>()
.x((_d, i) => x(i))
.y(d => y(d))
.curve(d3.curveMonotoneX);
// Color scale
const color = d3.scaleOrdinal<string>()
.domain(eegData.map(d => d.name))
.range(['#8884d8', '#82ca9d', '#ffc658', '#ff8042']);
// Add X axis
svg.append('g')
.attr('transform', `translate(0,${height})`)
.call(d3.axisBottom(x))
.selectAll('text')
.style('fill', 'var(--text)');
// Add Y axis
svg.append('g')
.call(d3.axisLeft(y))
.selectAll('text')
.style('fill', 'var(--text)');
// Add lines
eegData.forEach(d => {
svg.append('path')
.datum(d.values)
.attr('fill', 'none')
.attr('stroke', color(d.name))
.attr('stroke-width', 1.5)
.attr('d', line);
});
// Add legend at the top right
const legendItemHeight = 20; // Height allocated for each legend item
const legend = svg.append('g')
.attr('font-family', 'sans-serif')
.attr('font-size', 10)
.attr('text-anchor', 'end')
.selectAll('g')
.data(eegData)
.enter().append('g')
.attr('transform', (_d, i) => `translate(${width},${i * legendItemHeight + 10})`);
legend.append('rect')
.attr('x', -15)
.attr('width', 15)
.attr('height', 15)
.attr('fill', d => color(d.name));
legend.append('text')
.attr('x', -20)
.attr('y', 7.5)
.attr('dy', '0.32em')
.style('fill', 'var(--text)')
.text(d => d.name);
};
// Initial render
renderChart();
// Handle resize
const handleResize = () => {
if (!chartRef.current) return;
renderChart();
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, [chipInput]);
return <div ref={chartRef} className="w-full h-64 border border-gray-200 rounded-lg p-2"></div>;
};
// Vitals Chart Component
export const VitalsChart: React.FC<{ chipInput: ChipInput }> = ({chipInput}) => {
const chartRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!chartRef.current) return;
const vitalsData = [
{name: 'Puls', values: chipInput.puls},
{name: 'HRV', values: chipInput.hrv}
];
// Function to render the chart
const renderChart = () => {
if (!chartRef.current) return;
// Clear previous chart
d3.select(chartRef.current).selectAll('*').remove();
const margin = {top: 20, right: 20, bottom: 30, left: 50};
const width = chartRef.current.clientWidth - margin.left - margin.right;
const height = chartRef.current.clientHeight - margin.top - margin.bottom;
const svg = d3.select(chartRef.current)
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
// X scale
const x = d3.scaleLinear()
.domain([0, chipInput.puls.length - 1])
.range([0, width]);
// Y scale
const minValue = d3.min(vitalsData.flatMap(d => d.values)) || 0;
const maxValue = d3.max(vitalsData.flatMap(d => d.values)) || 100;
const y = d3.scaleLinear()
.domain([Math.max(0, minValue - 10), maxValue + 10])
.range([height, 0]);
// Line generator
const line = d3.line<number>()
.x((_d, i) => x(i))
.y(d => y(d))
.curve(d3.curveMonotoneX);
// Color scale
const color = d3.scaleOrdinal<string>()
.domain(vitalsData.map(d => d.name))
.range(['#ff6b6b', '#48dbfb']);
// Add X axis
svg.append('g')
.attr('transform', `translate(0,${height})`)
.call(d3.axisBottom(x))
.selectAll('text')
.style('fill', 'var(--text)');
// Add Y axis
svg.append('g')
.call(d3.axisLeft(y))
.selectAll('text')
.style('fill', 'var(--text)');
// Add lines
vitalsData.forEach(d => {
svg.append('path')
.datum(d.values)
.attr('fill', 'none')
.attr('stroke', color(d.name))
.attr('stroke-width', 1.5)
.attr('d', line);
});
// Add legend at the top right
const legendItemHeight = 20; // Height allocated for each legend item
const legend = svg.append('g')
.attr('font-family', 'sans-serif')
.attr('font-size', 10)
.attr('text-anchor', 'end')
.selectAll('g')
.data(vitalsData)
.enter().append('g')
.attr('transform', (_d, i) => `translate(${width},${i * legendItemHeight + 10})`);
legend.append('rect')
.attr('x', -15)
.attr('width', 15)
.attr('height', 15)
.attr('fill', d => color(d.name));
legend.append('text')
.attr('x', -20)
.attr('y', 7.5)
.attr('dy', '0.32em')
.style('fill', 'var(--text)')
.text(d => d.name);
};
// Initial render
renderChart();
// Handle resize
const handleResize = () => {
if (!chartRef.current) return;
renderChart();
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, [chipInput]);
return <div ref={chartRef} className="w-full h-64 border border-gray-200 rounded-lg p-2"></div>;
};
// Movement Chart Component
export const MovementChart: React.FC<{ chipInput: ChipInput }> = ({chipInput}) => {
const chartRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!chartRef.current) return;
const bewegungData = [
{name: 'Bewegung', values: chipInput.bewegung.map(v => v * 100)} // Scale movement for better visibility
];
// Function to render the chart
const renderChart = () => {
if (!chartRef.current) return;
// Clear previous chart
d3.select(chartRef.current).selectAll('*').remove();
const margin = {top: 20, right: 20, bottom: 30, left: 50};
const width = chartRef.current.clientWidth - margin.left - margin.right;
const height = chartRef.current.clientHeight - margin.top - margin.bottom;
const svg = d3.select(chartRef.current)
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
// X scale
const x = d3.scaleLinear()
.domain([0, chipInput.bewegung.length - 1])
.range([0, width]);
// Y scale
const y = d3.scaleLinear()
.domain([0, d3.max(bewegungData[0].values) || 100])
.range([height, 0]);
// Line generator
const line = d3.line<number>()
.x((_d, i) => x(i))
.y(d => y(d))
.curve(d3.curveMonotoneX);
// Color scale
const color = '#1dd1a1'; // Use the same color as before for consistency
// Add X axis
svg.append('g')
.attr('transform', `translate(0,${height})`)
.call(d3.axisBottom(x))
.selectAll('text')
.style('fill', 'var(--text)');
// Add Y axis
svg.append('g')
.call(d3.axisLeft(y))
.selectAll('text')
.style('fill', 'var(--text)');
// Add line
svg.append('path')
.datum(bewegungData[0].values)
.attr('fill', 'none')
.attr('stroke', color)
.attr('stroke-width', 1.5)
.attr('d', line);
// Add legend at the top right
const legendItemHeight = 20; // Height allocated for each legend item
const legend = svg.append('g')
.attr('font-family', 'sans-serif')
.attr('font-size', 10)
.attr('text-anchor', 'end')
.selectAll('g')
.data(bewegungData)
.enter().append('g')
.attr('transform', (_d, i) => `translate(${width},${i * legendItemHeight + 10})`);
legend.append('rect')
.attr('x', -15)
.attr('width', 15)
.attr('height', 15)
.attr('fill', () => color);
legend.append('text')
.attr('x', -20)
.attr('y', 7.5)
.attr('dy', '0.32em')
.style('fill', 'var(--text)')
.text(d => d.name);
};
// Initial render
renderChart();
// Handle resize
const handleResize = () => {
if (!chartRef.current) return;
renderChart();
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, [chipInput]);
return <div ref={chartRef} className="w-full h-64 border border-gray-200 rounded-lg p-2"></div>;
};

249
src/components/DreamVR.tsx Normal file
View File

@@ -0,0 +1,249 @@
import React, {useMemo, useRef} from 'react';
import {Canvas, useFrame} from '@react-three/fiber';
import {createXRStore, useXR, XR} from '@react-three/xr';
import {OrbitControls, PerspectiveCamera} from '@react-three/drei';
import * as THREE from 'three';
import type Dream from '../types/Dream';
// Neural Node component representing a synapse in the neural network
const NeuralNode = ({position, color, scale, pulseSpeed, pulseIntensity}: {
position: [number, number, number], color: string, scale: number, pulseSpeed: number, pulseIntensity: number
}) => {
const nodeRef = useRef<THREE.Mesh>(null);
const initialScale = scale;
useFrame(({clock}) => {
if (nodeRef.current) {
// Create a pulsing effect
const pulse = Math.sin(clock.getElapsedTime() * pulseSpeed) * pulseIntensity + 1;
nodeRef.current.scale.set(initialScale * pulse, initialScale * pulse, initialScale * pulse);
}
});
return (<mesh ref={nodeRef} position={position}>
<sphereGeometry args={[scale, 8, 8]}/>
<meshStandardMaterial color={color} emissive={color} emissiveIntensity={0.2}/>
</mesh>);
};
// Neural Connection component representing connections between synapses
const NeuralConnection = ({start, end, color, thickness, pulseSpeed, pulseIntensity}: {
start: [number, number, number],
end: [number, number, number],
color: string,
thickness: number,
pulseSpeed: number,
pulseIntensity: number
}) => {
const connectionRef = useRef<THREE.Mesh>(null);
// Create a cylinder between two points
const direction = useMemo(() => {
return new THREE.Vector3(end[0] - start[0], end[1] - start[1], end[2] - start[2]);
}, [start, end]);
const length = useMemo(() => direction.length(), [direction]);
// Calculate rotation to align cylinder with direction
useMemo(() => {
const normalizedDirection = direction.clone().normalize();
const quaternion = new THREE.Quaternion();
quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), // Default cylinder orientation
normalizedDirection);
return new THREE.Matrix4().makeRotationFromQuaternion(quaternion);
}, [direction]);
// Calculate position (midpoint between start and end)
const position = useMemo(() => {
return [(start[0] + end[0]) / 2, (start[1] + end[1]) / 2, (start[2] + end[2]) / 2] as [number, number, number];
}, [start, end]);
useFrame(({clock}) => {
if (connectionRef.current) {
// Create a pulsing effect for the connection
const pulse = Math.sin(clock.getElapsedTime() * pulseSpeed + (start[0] + start[1] + start[2])) * pulseIntensity + 1;
connectionRef.current.scale.set(thickness * pulse, length / 2, thickness * pulse);
}
});
return (<mesh ref={connectionRef} position={position}>
<cylinderGeometry args={[thickness, thickness, length]}/>
<meshStandardMaterial color={color} emissive={color} emissiveIntensity={0.1}/>
</mesh>);
};
// Neural Network component that generates nodes and connections
const NeuralNetwork = ({dream}: { dream: Dream }) => {
// Generate nodes and connections based on dream data
const nodes = useMemo(() => {
if (dream.input.inputType !== 'chip') return [];
const chipInput = dream.input;
const nodeCount = 50; // Number of nodes to generate
const nodes = [];
// Use EEG data to influence node positions and properties
for (let i = 0; i < nodeCount; i++) {
const alphaValue = chipInput.eeg.alpha[i % chipInput.eeg.alpha.length] / 50;
const betaValue = chipInput.eeg.beta[i % chipInput.eeg.beta.length] / 50;
const thetaValue = chipInput.eeg.theta[i % chipInput.eeg.theta.length] / 50;
// Calculate position based on EEG values
const x = (Math.random() - 0.5) * 20 + alphaValue * 10;
const y = (Math.random() - 0.5) * 20 + betaValue * 10;
const z = (Math.random() - 0.5) * 20 + thetaValue * 10;
// Determine color based on dominant wave
let color;
const dominantWave = Math.max(alphaValue, betaValue, thetaValue);
if (dominantWave === alphaValue) {
color = '#8884d8'; // Alpha - purple
} else if (dominantWave === betaValue) {
color = '#82ca9d'; // Beta - green
} else {
color = '#ffc658'; // Theta - yellow
}
// Scale based on pulse data
const scale = 0.2 + (chipInput.puls[i % chipInput.puls.length] / 200);
// Pulse speed and intensity based on HRV and movement
const pulseSpeed = 1 + (chipInput.hrv[i % chipInput.hrv.length] / 35);
const pulseIntensity = 0.1 + (chipInput.bewegung[i % chipInput.bewegung.length] * 2);
nodes.push({
position: [x, y, z] as [number, number, number], color, scale, pulseSpeed, pulseIntensity
});
}
return nodes;
}, [dream]);
// Generate connections between nodes
const connections = useMemo(() => {
if (dream.input.inputType !== 'chip') return [];
const connections = [];
const connectionCount = nodes.length * 2; // Each node connects to ~2 others
for (let i = 0; i < connectionCount; i++) {
const startNodeIndex = i % nodes.length;
// Connect to a nearby node (not completely random)
const endNodeIndex = (startNodeIndex + 1 + Math.floor(Math.random() * 5)) % nodes.length;
if (startNodeIndex !== endNodeIndex) {
connections.push({
start: nodes[startNodeIndex].position,
end: nodes[endNodeIndex].position,
color: nodes[startNodeIndex].color,
thickness: 0.05 + Math.random() * 0.05,
pulseSpeed: nodes[startNodeIndex].pulseSpeed,
pulseIntensity: nodes[startNodeIndex].pulseIntensity
});
}
}
return connections;
}, [nodes, dream]);
return (<>
{/* Render all nodes */}
{nodes.map((node, index) => (<NeuralNode
key={index}
position={node.position}
color={node.color}
scale={node.scale}
pulseSpeed={node.pulseSpeed}
pulseIntensity={node.pulseIntensity}
/>))}
{/* Render all connections */}
{connections.map((connection, index) => (<NeuralConnection
key={index}
start={connection.start}
end={connection.end}
color={connection.color}
thickness={connection.thickness}
pulseSpeed={connection.pulseSpeed}
pulseIntensity={connection.pulseIntensity}
/>))}
</>);
};
// Camera Controls component that handles XR and non-XR states
const CameraControls = () => {
const {session} = useXR();
// Only enable OrbitControls when NOT in VR session
if (session) {
return null; // Let XR handle camera in VR mode
}
return (<OrbitControls
enablePan={true}
enableZoom={true}
enableRotate={true}
enableDamping={true}
dampingFactor={0.05}
minDistance={5}
maxDistance={50}
maxPolarAngle={Math.PI}
minPolarAngle={0}
/>);
};
// Main DreamVR component
interface DreamVRProps {
dream: Dream;
height?: string;
}
// Create XR store outside the component to avoid recreation on each render
const store = createXRStore();
const DreamVR: React.FC<DreamVRProps> = ({dream, height = '500px'}) => {
const containerRef = useRef<HTMLDivElement>(null);
// Only render VR for dream with chip input type
if (dream.input.inputType !== 'chip') {
return (<div className="flex items-center justify-center h-full text-gray-500">
VR-Visualisierung ist nur für Träume mit Chip-Eingabe verfügbar.
</div>);
}
return (<div ref={containerRef} className="relative w-full" style={{height}}>
{/* VR Entry Button */}
<button
onClick={() => store.enterVR()}
className="absolute top-4 right-4 z-10 p-2 bg-white/20 backdrop-blur-sm rounded-full text-white hover:bg-white/30 transition-colors"
aria-label="Enter VR"
>
🥽 VR
</button>
<Canvas camera={{position: [0, 0, 10], fov: 60}}>
<XR store={store}>
{/* Camera setup */}
<PerspectiveCamera makeDefault position={[0, 0, 10]}/>
{/* Camera Controls - only active outside VR */}
<CameraControls/>
{/* Lighting */}
<ambientLight intensity={0.2}/>
<pointLight position={[10, 10, 10]} intensity={0.8}/>
<pointLight position={[-10, -10, -10]} intensity={0.4}/>
{/* Neural network visualization */}
<NeuralNetwork dream={dream}/>
{/* Background */}
<mesh scale={[100, 100, 100]}>
<sphereGeometry args={[1, 32, 32]}/>
<meshBasicMaterial color="#000012" side={THREE.BackSide}/>
</mesh>
</XR>
</Canvas>
</div>);
};
export default DreamVR;

View File

@@ -10,7 +10,7 @@ interface StudyCardProps {
institution: string;
status: string;
statusColor: string;
participants: {
participants?: {
current: number;
target: number;
};
@@ -22,7 +22,7 @@ interface StudyCardProps {
export const StudyCard: React.FC<StudyCardProps> = ({study}) => {
return (
<div className="dreamy-card p-6" style={getBackgroundStyle(study.color)}>
<div className="dreamy-card p-6 flex flex-col h-full" style={getBackgroundStyle(study.color)}>
<div className="flex items-center mb-4">
<IconWithBackground
icon={<FaFlask/>}
@@ -44,12 +44,14 @@ export const StudyCard: React.FC<StudyCardProps> = ({study}) => {
{study.status}
</span>
</div>
<div className="flex justify-between mb-2">
<span className="text-sm font-bold">Teilnehmer:</span>
<span className="text-sm">
{study.participants && (
<div className="flex justify-between mb-2">
<span className="text-sm font-bold">Teilnehmer:</span>
<span className="text-sm">
{study.participants.current.toLocaleString()} / {study.participants.target.toLocaleString()}
</span>
</div>
</div>
)}
<div className="flex justify-between">
<span className="text-sm font-bold">Enddatum:</span>
<span className="text-sm">{study.endDate}</span>
@@ -61,7 +63,7 @@ export const StudyCard: React.FC<StudyCardProps> = ({study}) => {
</p>
<button
className={`w-full py-2 px-4 bg-${study.color}-500 hover:bg-${study.color}-600 text-white rounded-lg transition-colors`}>
className={`w-full py-2 px-4 bg-${study.color}-500 hover:bg-${study.color}-600 text-white rounded-lg transition-colors mt-auto`}>
Teilnehmen
</button>
</div>

View File

@@ -1,4 +1,4 @@
import Dream, {type ImageInput, type TextInput} from '../types/Dream.ts';
import Dream, {type AudioInput, type ChipInput, type ImageInput, type TextInput} from '../types/Dream.ts';
// Function to generate a random date between July 3rd, 2025 and July 16th, 2025
function getRandomDateInRange() {
@@ -8,122 +8,239 @@ function getRandomDateInRange() {
return new Date(randomTime);
}
export const mockDreams: Dream[] = [new Dream({
id: 1, userId: 1, title: 'Mitternachtsbibliothek', date: getRandomDateInRange(), input: {
inputType: 'text',
input: 'Ich wanderte um Mitternacht in eine riesige Bibliothek, in der jedes Buch sanft leuchtete. Als ich eines aus dem Regal zog, flüsterten seine Seiten mir Geheimnisse zu.',
} as TextInput, ai: {
interpretation: 'Dieses Bild symbolisiert den Wunsch nach Wissen und die Faszination für ungelüftete Geheimnisse. Die leuchtenden Bücher stehen für Inspiration und Erkenntnis, während die nächtliche Atmosphäre auf Introspektion und das Unbewusste hinweist.',
image: '01.png',
video: '01.mp4',
audio: '01.mp3',
}
}), new Dream({
id: 2, userId: 2, title: 'Unterwasserkonzert', date: getRandomDateInRange(), input: {
inputType: 'text',
input: 'Ich besuchte ein Konzert unter dem Ozean. Die Musiker waren Delfine, die Melodien anklickten, während Korallenpolypen im Takt zum Rhythmus mit Licht pulsierten.',
} as TextInput, ai: {
interpretation: 'Das Unterwasserkonzert kann als Ausdruck von Kreativität und Harmonie gedeutet werden. Delfine symbolisieren Intelligenz und Spielfreude, Korallenlicht verweist auf emotionale Schwingungen und die Verbindung zum Inneren.',
image: '02.png',
video: '02.mp4',
}
}), new Dream({
id: 3, userId: 3, title: 'Wüstenzug', date: getRandomDateInRange(), input: {
inputType: 'text',
input: 'Ein Dampflok-Express tuckerte über goldene Dünen unter einem violetten Himmel. Passagiere winkten, während Kamele mit Laternen neben den Schienen herliefen.',
} as TextInput, ai: {
interpretation: 'Der Wüstenzug steht für eine Reise durch unbewusste Räume und persönliche Herausforderungen (Dünen). Der violette Himmel deutet auf Spiritualität hin, während die Laternen der Kamele Hoffnung und Wegweisung symbolisieren.',
image: '03.png',
video: '03.mp4',
}
}), new Dream({
id: 4, userId: 1, title: 'Schwebende Teeparty', date: getRandomDateInRange(), input: {
inputType: 'text',
input: 'Ich saß mit Freunden auf einer Wolke zu einer Teeparty. Jede Tasse war mit Sternenstaub gefüllt, und der Himmel um uns herum schimmerte in pastelligen Regenbogenfarben.',
} as TextInput, ai: {
interpretation: 'Diese Szene steht für Geborgenheit und Gemeinschaft auf einer höheren Ebene. Der Sternenstaub in den Tassen symbolisiert geteilte Träume, die pastelligen Regenbögen zeigen eine optimistische Grundstimmung und Leichtigkeit.',
image: '04.png'
}
}), new Dream({
id: 5, userId: 2, title: 'Spiegelgarten', date: getRandomDateInRange(), input: {
inputType: 'text',
input: 'Ich erkundete einen Garten aus Spiegeln, in denen jeweils eine andere Kindheitserinnerung reflektiert wurde. Als ich einen berührte, hörte ich entferntes Lachen durch das Glas hallen.',
} as TextInput, ai: {
interpretation: 'Der Spiegelgarten repräsentiert Selbsterkenntnis und Rückblick auf die eigene Vergangenheit. Jeder Spiegel steht für ein Fragment der Erinnerung, das entfernte Lachen deutet auf positive Prägungen und emotionale Verankerung hin.',
image: '05.png',
audio: '05.mp3'
}
}), new Dream({
id: 6, userId: 1, title: 'Crystal Forest', date: getRandomDateInRange(), input: {
inputType: 'image',
img: 'crystal_forest.jpg',
imgAlt: 'A glowing forest made of crystals under a starry sky',
description: 'I wandered through a glowing forest where every tree was made of shimmering crystal, reflecting thousands of stars above.',
} as ImageInput, ai: {
interpretation: 'The crystal forest represents clarity and the search for inner truth. The reflections of stars symbolize inspiration and the connection between your dreams and aspirations.',
audio: 'crystal_forest.mp3',
image: 'crystal_forest_ai.png',
},
}), new Dream({
id: 7, userId: 2, title: 'Mirror Lake', date: getRandomDateInRange(), input: {
inputType: 'image',
img: 'mirror_lake.jpg',
imgAlt: 'A perfectly still lake reflecting the moon and clouds',
description: 'I stood at the edge of a silent lake, its surface so calm it looked like a perfect mirror, reflecting the moon and drifting clouds.',
} as ImageInput, ai: {
interpretation: 'The mirror lake suggests self-reflection and emotional calm. The moon and clouds indicate subconscious thoughts and the ever-changing nature of your feelings.',
video: 'mirror_lake_ai.mp4',
image: 'mirror_lake_ai.png',
},
}), new Dream({
id: 8, userId: 3, title: 'Floating City', date: getRandomDateInRange(), input: {
inputType: 'image',
img: 'floating_city.jpg',
imgAlt: 'A futuristic city floating above the clouds at sunrise',
description: 'I explored a futuristic city that floated above the clouds, with golden sunlight streaming through transparent walkways.',
} as ImageInput, ai: {
interpretation: 'The floating city symbolizes ambition and the desire to rise above limitations. The sunrise and golden light represent hope and new beginnings.',
image: 'floating_city_ai.png',
},
}), new Dream({
id: 9, userId: 4, title: 'Quantenreise', date: getRandomDateInRange(), input: {
inputType: 'text',
input: 'Ich reiste durch einen Tunnel aus pulsierenden Datenströmen. Jede Berührung löste neue Realitäten aus, die sich wie Fraktale vor mir entfalteten.',
} as TextInput, ai: {
interpretation: 'Die Quantenreise symbolisiert deine Faszination für Möglichkeiten und Parallelwelten. Die Datenströme repräsentieren Informationsverarbeitung und Entscheidungsfindung in deinem Unterbewusstsein.',
image: 'quantum_journey.png',
video: 'quantum_journey.mp4',
}
}), new Dream({
id: 10, userId: 4, title: 'Neuronales Netzwerk', date: getRandomDateInRange(), input: {
inputType: 'text',
input: 'Ich befand mich in einem riesigen neuronalen Netzwerk, wo Gedanken als leuchtende Impulse zwischen synaptischen Verbindungen tanzten. Ich konnte sie mit meinen Händen formen und lenken.',
} as TextInput, ai: {
interpretation: 'Das neuronale Netzwerk spiegelt dein Interesse an künstlicher Intelligenz und dem menschlichen Bewusstsein wider. Die Fähigkeit, Gedanken zu formen, deutet auf deinen Wunsch nach Kontrolle über deine mentalen Prozesse hin.',
image: 'neural_network.png',
audio: 'neural_network.mp3',
}
}), new Dream({
id: 11, userId: 4, title: 'Digitaler Garten', date: getRandomDateInRange(), input: {
inputType: 'image',
img: 'digital_garden.jpg',
imgAlt: 'Ein Garten aus leuchtenden Codezeilen und holografischen Pflanzen',
description: 'Ich pflegte einen Garten, in dem Pflanzen aus Codezeilen und Algorithmen wuchsen. Jede Blüte entfaltete sich zu einer neuen Technologie.',
} as ImageInput, ai: {
interpretation: 'Der digitale Garten verkörpert deine kreative Verbindung von Natur und Technologie. Er zeigt, wie du Innovation als organischen, wachsenden Prozess betrachtest.',
image: 'digital_garden_ai.png',
}
}), new Dream({
id: 12, userId: 4, title: 'Zeitschleife', date: getRandomDateInRange(), input: {
inputType: 'text',
input: 'Ich erlebte denselben Tag immer wieder, konnte aber jedes Mal kleine Änderungen vornehmen. Mit jeder Iteration wurde die Realität komplexer und unvorhersehbarer.',
} as TextInput, ai: {
interpretation: 'Die Zeitschleife reflektiert deine Gedanken über Kausalität und die Auswirkungen kleiner Entscheidungen. Sie symbolisiert auch den Wunsch, Fehler zu korrigieren und verschiedene Möglichkeiten zu erkunden.',
image: 'time_loop.png',
audio: 'time_loop.mp3',
}
}),
export const mockDreams: Dream[] = [
// Traum 1
new Dream({
id: 1,
userId: 1,
title: 'Mitternachtsbibliothek',
date: new Date('2025-07-17T03:25:57Z'),
tags: ['Wissen', 'Geheimnisse', 'Inspiration'],
input: {
inputType: 'text',
input: 'Ich wanderte um Mitternacht in eine riesige Bibliothek, in der jedes Buch sanft leuchtete. Als ich eines aus dem Regal zog, flüsterten seine Seiten mir Geheimnisse zu.'
} as TextInput,
ai: {
interpretation: 'Dieses Bild symbolisiert den Wunsch nach Wissen und die Faszination für ungelüftete Geheimnisse. Die leuchtenden Bücher stehen für Inspiration und Erkenntnis, während die nächtliche Atmosphäre auf Introspektion und das Unbewusste hinweist.',
image: '01.png',
video: '01.mp4',
audio: '01.mp3'
}
}),
];
// Traum 2
new Dream({
id: 2,
userId: 2,
title: 'Unterwasserkonzert',
date: getRandomDateInRange(),
tags: ['Kreativität', 'Harmonie', 'Ozean'],
input: {
inputType: 'audio',
transcript: 'Ich besuchte ein Konzert unter dem Ozean. Die Musiker waren Delfine, die Melodien anklickten, während Korallenpolypen im Takt zum Rhythmus mit Licht pulsierten.',
audio: 'in_02.mp3'
} as AudioInput,
ai: {
interpretation: 'Das Unterwasserkonzert kann als Ausdruck von Kreativität und Harmonie gedeutet werden. Delfine symbolisieren Intelligenz und Spielfreude, Korallenlicht verweist auf emotionale Schwingungen und die Verbindung zum Inneren.',
image: '02.png',
audio: '02.mp3',
video: '02.mp4'
}
}),
export default mockDreams;
// Traum 3
new Dream({
id: 3,
userId: 3,
title: 'Wüstenzug',
date: getRandomDateInRange(),
tags: ['Reise', 'Herausforderung', 'Spiritualität'],
input: {
inputType: 'image',
description: 'Ein Dampflok-Express tuckerte über goldene Dünen unter einem violetten Himmel. Passagiere winkten, während Kamele mit Laternen neben den Schienen herliefen.',
img: 'in_03.png',
imgAlt: ''
} as ImageInput,
ai: {
interpretation: 'Der Wüstenzug steht für eine Reise durch unbewusste Räume und persönliche Herausforderungen (Dünen). Der violette Himmel deutet auf Spiritualität hin, während die Laternen der Kamele Hoffnung und Wegweisung symbolisieren.',
image: '03.png',
video: '03.mp4',
audio: '03.mp3'
}
}),
// Traum 4
new Dream({
id: 4,
userId: 1,
title: 'Schwebende Teeparty',
date: getRandomDateInRange(),
tags: ['Gemeinschaft', 'Geborgenheit', 'Leichtigkeit'],
input: {
inputType: 'audio',
transcript: 'Ich saß mit Freunden auf einer Wolke zu einer Teeparty. Jede Tasse war mit Sternenstaub gefüllt, und der Himmel um uns herum schimmerte in pastelligen Regenbogenfarben.',
audio: 'in_04.mp3',
} as AudioInput,
ai: {
interpretation: 'Diese Szene steht für Geborgenheit und Gemeinschaft auf einer höheren Ebene. Der Sternenstaub in den Tassen symbolisiert geteilte Träume, die pastelligen Regenbögen zeigen eine optimistische Grundstimmung und Leichtigkeit.',
image: '04.png',
audio: '04.mp3',
video: '04.mp4'
}
}),
// Traum 5
new Dream({
id: 5,
userId: 2,
title: 'Spiegelgarten',
date: getRandomDateInRange(),
tags: ['Selbsterkenntnis', 'Erinnerung', 'Vergangenheit'],
input: {
inputType: 'text',
input: 'Ich erkundete einen Garten aus Spiegeln, in denen jeweils eine andere Kindheitserinnerung reflektiert wurde. Als ich einen berührte, hörte ich entferntes Lachen durch das Glas hallen.'
} as TextInput,
ai: {
interpretation: 'Der Spiegelgarten repräsentiert Selbsterkenntnis und Rückblick auf die eigene Vergangenheit. Jeder Spiegel steht für ein Fragment der Erinnerung, das entfernte Lachen deutet auf positive Prägungen und emotionale Verankerung hin.',
image: '05.png',
audio: '05.mp3',
video: '05.mp4'
}
}),
// Traum 9 (userId 4)
new Dream({
id: 9,
userId: 4,
title: 'Quantenreise',
date: getRandomDateInRange(),
tags: ['Faszination', 'Parallelwelten', 'Informationsverarbeitung'],
input: {
inputType: 'text',
input: 'Ich reiste durch einen Tunnel aus pulsierenden Datenströmen. Jede Berührung löste neue Realitäten aus, die sich wie Fraktale vor mir entfalteten.'
} as TextInput,
ai: {
interpretation: 'Die Quantenreise symbolisiert deine Faszination für Möglichkeiten und Parallelwelten. Die Datenströme repräsentieren Informationsverarbeitung und Entscheidungsfindung in deinem Unterbewusstsein.',
image: '09.png',
audio: '09.mp3',
video: '09.mp4'
}
}),
// Traum 10 (userId 4)
new Dream({
id: 10,
userId: 4,
title: 'Neuronales Netzwerk',
date: new Date('2025-07-17T05:28:57Z'),
tags: ['KI', 'Bewusstsein', 'Kontrolle'],
input: {
inputType: 'chip',
text: 'Du hast von einem riesigen neuronalen Netzwerk, wo Gedanken als leuchtende Impulse zwischen synaptischen Verbindungen tanzten, geträumt.',
eeg: {
alpha: [
21.49, 19.59, 21.94, 24.57, 19.30, 19.30, 24.74, 22.30, 18.59, 21.63,
18.61, 18.60, 20.73, 14.26, 14.83, 18.31, 16.96, 20.94, 17.28, 15.76,
24.40, 19.32, 20.20, 15.73, 18.37, 20.33, 16.55, 21.13, 18.20, 19.12,
18.19, 25.56, 19.96, 16.83, 22.47, 16.34, 20.63, 14.12, 16.02, 20.59,
22.22, 20.51, 19.65, 19.10, 15.56, 17.84, 18.62, 23.17, 21.03, 14.71,
20.97, 18.84, 17.97, 21.84, 23.09, 22.79, 17.48, 19.07, 20.99, 22.93
],
beta: [
9.04, 9.63, 7.79, 7.61, 11.63, 12.71, 9.86, 12.01, 10.72, 8.71,
10.72, 13.08, 9.93, 13.13, 5.00, 11.64, 10.17, 9.40, 10.18, 6.02,
9.56, 10.71, 12.96, 8.96, 8.38, 8.99, 11.83, 10.66, 8.94, 11.03,
10.19, 11.94, 8.60, 9.34, 9.22, 7.07, 10.59, 10.52, 10.01, 9.53,
7.17, 9.16, 9.31, 8.40, 9.68, 10.81, 13.77, 10.35, 10.52, 9.85,
6.16, 9.95, 10.12, 14.93, 9.62, 10.60, 9.93, 7.66, 12.29, 11.50
],
theta: [
33.96, 25.45, 37.01, 22.99, 32.93, 40.00, 25.05, 27.17, 30.50, 27.48,
22.25, 30.34, 24.69, 32.37, 25.40, 37.75, 26.08, 28.39, 34.07, 23.85,
31.14, 36.54, 21.96, 30.92, 31.30, 33.91, 23.82, 23.40, 32.61, 31.48,
31.25, 31.73, 26.60, 31.16, 31.47, 26.43, 39.33, 32.37, 24.04, 33.28,
25.13, 33.94, 35.79, 25.90, 34.82, 32.06, 34.11, 39.48, 28.77, 26.23,
25.55, 25.92, 29.61, 31.71, 31.38, 34.13, 30.07, 37.27, 28.68, 40.00
],
delta: [
43.75, 34.86, 33.57, 42.89, 38.66, 44.28, 42.84, 39.56, 34.92, 30.91,
37.32, 45.14, 41.28, 32.53, 41.04, 42.31, 34.70, 40.92, 40.35, 33.14,
42.15, 43.36, 46.50, 46.32, 31.73, 34.37, 43.09, 43.08, 43.09, 50.00,
43.43, 46.81, 45.72, 43.91, 38.11, 44.55, 35.36, 38.58, 37.09, 40.49,
50.00, 30.00, 44.12, 30.32, 37.17, 46.53, 40.39, 33.53, 35.71, 44.08,
35.62, 41.30, 40.27, 36.09, 50.00, 43.80, 30.00, 41.12, 36.03, 45.11
]
},
puls: [
62.62, 64.66, 66.51, 67.60, 61.40, 64.00, 63.58, 63.04, 70.00, 66.21,
61.22, 67.75, 70.00, 68.10, 60.44, 63.55, 68.80, 62.88, 66.33, 67.32,
62.22, 64.82, 60.00, 61.93, 64.24, 61.26, 69.90, 60.71, 63.68, 65.39,
69.32, 60.69, 68.49, 65.03, 62.06, 66.39, 65.60, 63.20, 65.21, 63.84,
65.34, 66.99, 69.76, 61.29, 70.00, 60.00, 64.54, 66.76, 65.84, 63.13,
64.38, 63.52, 63.23, 67.55, 66.07, 62.92, 67.70, 65.92, 67.44, 66.89
],
hrv: [
60.61, 57.89, 60.71, 57.86, 67.10, 62.59, 69.24, 60.96, 52.74, 56.56,
66.48, 59.99, 54.13, 57.87, 70.00, 63.01, 55.81, 62.69, 58.96, 54.70,
66.05, 63.33, 65.91, 70.00, 56.49, 61.42, 56.00, 70.00, 65.76, 62.36,
51.29, 64.13, 53.40, 56.55, 70.00, 56.49, 54.68, 61.99, 61.65, 59.49,
55.21, 57.25, 51.71, 64.89, 47.64, 70.00, 53.67, 49.43, 54.72, 62.67,
62.80, 70.00, 67.82, 54.10, 57.76, 59.14, 54.51, 56.71, 56.74, 52.09
],
bewegung: [
0.20, 0.30, 0.14, 0.19, 0.22, 0.11, 0.17, 0.15, 0.16, 0.07,
0.15, 0.20, 0.30, 0.25, 0.37, 0.07, 0.24, 0.17, 0.37, 0.07,
0.07, 0.09, 0.00, 0.10, 0.07, 0.17, 0.18, 0.34, 0.25, 0.09,
0.06, 0.20, 0.02, 0.33, 0.27, 0.10, 0.00, 0.29, 0.14, 0.27,
0.00, 0.09, 0.15, 0.15, 0.10, 0.21, 0.04, 0.14, 0.16, 0.20,
0.22, 0.04, 0.00, 0.28, 0.18, 0.08, 0.31, 0.16, 0.27, 0.16
]
} as ChipInput,
ai: {
interpretation: 'Das neuronale Netzwerk spiegelt dein Interesse an künstlicher Intelligenz und dem menschlichen Bewusstsein wider. Die Fähigkeit, Gedanken zu formen, deutet auf deinen Wunsch nach Kontrolle über deine mentalen Prozesse hin.',
audio: '10.mp3',
image: '10.png',
video: '10.mp4'
}
}),
// Traum 11 (userId 4)
new Dream({
id: 11,
userId: 4,
title: 'Digitaler Garten',
date: getRandomDateInRange(),
tags: ['Natur', 'Technologie', 'Innovation'],
input: {
inputType: 'image',
img: 'in_11.png',
imgAlt: 'Ein Garten aus leuchtenden Codezeilen und holografischen Pflanzen',
description: 'Leuchtende, halb-transparente Pflanzen mit holografischen Mustern wachsen in einem futuristischen Garten, in dem organische Formen und digitale Technologien harmonisch verschmelzen. Farbenfrohe Lichter ziehen sich an den Ästen entlang und schaffen eine friedliche, zukunftsweisende Atmosphäre.'
} as ImageInput,
ai: {
interpretation: 'Der digitale Garten verkörpert deine kreative Verbindung von Natur und Technologie. Er zeigt, wie du Innovation als organischen, wachsenden Prozess betrachtest.',
audio: '11.mp3',
image: '11.png',
video: '11.mp4'
}
}),
// Traum 12 (userId 4)
new Dream({
id: 12,
userId: 4,
title: 'Zeitschleife',
date: getRandomDateInRange(),
tags: ['Kausalität', 'Entscheidungen', 'Realität'],
input: {
inputType: 'text',
input: 'Ich erlebte denselben Tag immer wieder, konnte aber jedes Mal kleine Änderungen vornehmen. Mit jeder Iteration wurde die Realität komplexer und unvorhersehbarer.'
} as TextInput,
ai: {
interpretation: 'Die Zeitschleife reflektiert deine Gedanken über Kausalität und die Auswirkungen kleiner Entscheidungen. Sie symbolisiert auch den Wunsch, Fehler zu korrigieren und verschiedene Möglichkeiten zu erkunden.',
audio: '12.mp3',
image: '12.png',
video: '12.mp4'
}
})
];

View File

@@ -1,236 +1,144 @@
/**
* Mock data for the ResearchLive page
* Contains information about current studies, citizen science projects,
* researcher interviews, and upcoming events
*/
// Updated MockResearchLive.ts
// Define color type to match component requirements
type ColorType = 'purple' | 'blue' | 'violet' | 'emerald' | 'amber' | 'rose' | 'pink-red' | 'cta';
// Define study type
interface Study {
id: number;
title: string;
institution: string;
status: string;
statusColor: string;
participants: {
current: number;
target: number;
};
participants?: { current: number; target: number };
endDate: string;
description: string;
color: ColorType;
}
// Define researcher interview type
interface ResearcherInterview {
interface CitizenScienceProject {
id: number;
name: string;
institution: string;
specialty: string;
topics: string[];
color: ColorType;
videoId: string;
title: string;
icon: string;
description: string;
optedIn: boolean;
}
interface Event {
id: number;
date: string;
title: string;
location: string;
type: string;
typeColor: string;
}
// Current Studies data
export const currentStudies: Study[] = [
{
id: 1,
title: "Traumkontinuität während der Pandemie",
institution: "Universität Zürich, Schweiz",
status: "Aktiv",
statusColor: "green",
participants: {
current: 1245,
target: 2000
},
endDate: "31. Dezember 2024",
description: "Diese Langzeitstudie untersucht, wie sich Träume während und nach der COVID-19-Pandemie verändert haben und ob Traumthemen mit realen Ereignissen korrelieren.",
color: "purple"
},
{
id: 2,
title: "Luzide Träume und Kreativität",
institution: "Stanford University, USA",
status: "Aktiv",
statusColor: "green",
participants: {
current: 876,
target: 1000
},
endDate: "15. März 2025",
description: "Diese Studie untersucht den Zusammenhang zwischen der Fähigkeit, luzide Träume zu erleben, und kreativen Problemlösungsfähigkeiten im Wachzustand.",
color: "blue"
},
{
id: 3,
title: "Traumemotionen und mentale Gesundheit",
institution: "Universität Wien, Österreich",
status: "Fast voll",
statusColor: "yellow",
participants: {
current: 1890,
target: 2000
},
endDate: "30. September 2024",
description: "Diese Studie erforscht die Beziehung zwischen emotionalen Mustern in Träumen und verschiedenen Aspekten der psychischen Gesundheit im Wachzustand.",
color: "emerald"
},
{
id: 4,
title: "Kulturübergreifende Traumsymbole",
institution: "University of Tokyo, Japan",
status: "Aktiv",
statusColor: "green",
participants: {
current: 3456,
target: 10000
},
participants: {current: 3456, target: 10000},
endDate: "31. Dezember 2025",
description: "Diese globale Studie sammelt Traumsymbole aus verschiedenen Kulturen, um universelle und kulturspezifische Traumelemente zu identifizieren.",
color: "amber"
}
description:
"Diese globale Studie sammelt Traumsymbole aus verschiedenen Kulturen, um universelle und kulturspezifische Traumelemente zu identifizieren.",
color: "amber",
},
{
id: 2,
title: "Time course of the lucid dream experience",
institution: "Organization for Lucid Dream Studies",
status: "Laufend",
statusColor: "green",
endDate: "April 2026",
description:
"Untersuchung, wann und wie Träume in luzide Träume übergehen und was danach geschieht.",
color: "violet",
},
{
id: 3,
title: "Reality monitoring & state test reliability",
institution: "Organization for Lucid Dream Studies",
status: "Laufend",
statusColor: "green",
endDate: "August 2027",
description:
"Vergleich von Reality-Monitoring-Fehlern und Traum-Wach-Verwechslungen in Bezug auf die Häufigkeit luzider Träume.",
color: "pink-red",
},
{
id: 4,
title: "Rapid Eye Movement Restoration and Enhancement for Sleep-deprived Trauma-adaptation",
institution: "University of WisconsinMadison, USA",
status: "Laufend",
statusColor: "green",
endDate: "31. August 2025",
description:
"Untersuchung, ob gezielte REM-Schlaf-Wiederherstellung und -Förderung bei schlafentzugbedingten Traumafolgestörungen die Symptome lindert und die Erholung verbessert.",
color: "emerald",
},
];
// Citizen Science projects data
export const citizenScienceProjects = [
export const citizenScienceProjects: CitizenScienceProject[] = [
{
id: 1,
title: "Globales Traumtagebuch",
icon: "FaClipboardCheck",
description: "Teile deine Träume in der weltweit größten Traumdatenbank und hilf Forschern, Muster zu erkennen.",
optedIn: true
description:
"Teile deine Träume in der weltweit größten Traumdatenbank und hilf Forschern, Muster zu erkennen.",
optedIn: true,
},
{
id: 2,
title: "Traumkarte",
icon: "FaGlobeAmericas",
description: "Hilf bei der Erstellung einer interaktiven Weltkarte, die zeigt, wie Träume je nach Region variieren.",
optedIn: false
icon: "FaGlobeEurope",
description:
"Hilf bei der Erstellung einer interaktiven Weltkarte, die zeigt, wie Träume je nach Region variieren.",
optedIn: false,
},
{
id: 3,
title: "30-Tage-Challenge",
icon: "FaCalendarAlt",
description: "Nimm an der 30-Tage-Traumaufzeichnungs-Challenge teil und erhalte personalisierte Analysen.",
optedIn: false
title: "Kulturelle Traumlandschaften",
icon: "FaUsers",
description:
"Teile deine demographischen Daten und hilf bei der Untersuchung der Auswirkung von Kultur aufs Träumen.",
optedIn: false,
}
];
// Researcher Interviews data
export const researcherInterviews: ResearcherInterview[] = [
export const upcomingEvents: Event[] = [
{
id: 1,
name: "Dr. Sarah Merton",
institution: "Harvard University, USA",
specialty: "Expertin für Traumkognition und neuronale Korrelate des Träumens",
topics: [
"Neueste Erkenntnisse zur Traumkognition",
"Wie das Gehirn Traumnarrative konstruiert",
"Die Rolle von REM-Schlaf bei der Gedächtniskonsolidierung"
],
color: "blue",
videoId: "sarah_merton_interview"
date: "16. August 2025",
title: "Regional Dream Conference",
location: "Ort wird noch bekanntgegeben",
type: "Konferenz",
typeColor: "emerald",
},
{
id: 2,
name: "Prof. Dr. Markus Weber",
institution: "Max-Planck-Institut, Deutschland",
specialty: "Pionier in der Erforschung luzider Träume und Traumkontrolle",
topics: [
"Techniken zur Induktion luzider Träume",
"Therapeutische Anwendungen von Traumkontrolle",
"Ethische Fragen in der Traummanipulation"
],
color: "emerald",
videoId: "markus_weber_interview"
date: "26. September 2025 27. September 2025",
title: "Regional Dream Conference",
location: "Bridgewater State University, USA",
type: "Konferenz",
typeColor: "emerald",
},
{
id: 3,
name: "Dr. Yuki Tanaka",
institution: "Kyoto University, Japan",
specialty: "Spezialistin für kulturelle Traumforschung und vergleichende Traumanalyse",
topics: [
"Kulturelle Unterschiede in Traumsymbolen",
"Einfluss von Mythen und Folklore auf Träume",
"Universelle vs. kulturspezifische Traumelemente"
],
color: "amber",
videoId: "yuki_tanaka_interview"
date: "10. Oktober 2025 12. Oktober 2025",
title: "Internationale Jahreskonferenz der IASD",
location: "Ort wird noch bekanntgegeben",
type: "Konferenz",
typeColor: "emerald",
},
{
id: 4,
name: "Dr. Elena Rodriguez",
institution: "Universidad de Barcelona, Spanien",
specialty: "Forscherin im Bereich Traumtherapie und emotionale Verarbeitung",
topics: [
"Traumbasierte Therapieansätze",
"Verarbeitung traumatischer Erlebnisse im Traum",
"Emotionale Regulation durch gezielte Traumarbeit"
],
color: "rose",
videoId: "elena_rodriguez_interview"
}
];
// Upcoming Events data
export const upcomingEvents = [
{
id: 1,
date: "15. Juli 2024",
title: "Internationale Konferenz für Traumforschung",
location: "Berlin, Deutschland",
type: "Konferenz",
typeColor: "blue"
},
{
id: 2,
date: "3. August 2024",
title: "Workshop: Luzide Träume für Anfänger",
location: "Online",
type: "Workshop",
typeColor: "green"
},
{
id: 3,
date: "22. September 2024",
title: "Symposium: Träume und psychische Gesundheit",
location: "Wien, Österreich",
type: "Symposium",
typeColor: "amber"
},
{
id: 4,
date: "10. Oktober 2024",
title: "Webinar: Neueste Technologien in der Traumforschung",
location: "Online",
type: "Webinar",
typeColor: "purple"
},
{
id: 5,
date: "5. November 2024",
title: "Traumforschung 2025: Ausblick und Trends",
location: "Paris, Frankreich",
type: "Konferenz",
typeColor: "rose"
}
];
// Define the type for the exported object
interface MockResearchLiveData {
currentStudies: Study[];
citizenScienceProjects: any[]; // Using any for now as we haven't defined a specific type
researcherInterviews: ResearcherInterview[];
upcomingEvents: any[]; // Using any for now as we haven't defined a specific type
}
// Export with type information
export default {
currentStudies,
citizenScienceProjects,
researcherInterviews,
upcomingEvents
} as MockResearchLiveData;
} as {
currentStudies: Study[];
citizenScienceProjects: CitizenScienceProject[];
upcomingEvents: Event[];
};

View File

@@ -6,9 +6,14 @@ import MockUsers from '../data/MockUsers';
import User from '../types/User';
import type Dream from "../types/Dream.ts";
import {formatDateNumeric, formatDateWithTime} from '../utils/DateUtils';
import Slider from 'react-slick';
import 'slick-carousel/slick/slick.css';
import 'slick-carousel/slick/slick-theme.css';
import {lazy, Suspense} from 'react';
// Lazy load components
const DreamVR = lazy(() => import('../components/DreamVR'));
const SliderComponent = lazy(() => import('react-slick'));
const EEGChart = lazy(() => import('../components/DreamCharts').then(module => ({default: module.EEGChart})));
const VitalsChart = lazy(() => import('../components/DreamCharts').then(module => ({default: module.VitalsChart})));
const MovementChart = lazy(() => import('../components/DreamCharts').then(module => ({default: module.MovementChart})));
export default function DreamPage() {
const {id} = useParams<{ id: string }>();
@@ -17,6 +22,7 @@ export default function DreamPage() {
const dream: Dream | undefined = mockDreams.find(d => d.id === Number(id));
const user: User | undefined = dream ? MockUsers.find(u => u.id === dream.userId) : undefined;
if (!dream) {
return (<div className="page p-4">
<button
@@ -64,14 +70,74 @@ export default function DreamPage() {
<div className="flex items-center mb-4">
<span className="font-medium dreamy-text">Traum-Beschreibung</span>
</div>
{(dream.input.inputType === 'image' || dream.input.inputType === 'audio') && (
<div className="flex justify-center mb-1">
{dream.input.inputType === 'audio' && (<audio></audio>)}
{dream.input.inputType === 'image' && (<img alt={dream.input.imgAlt}></img>)}
</div>)}
<p className="leading-relaxed text-base sm:text-lg">
{(dream.input.inputType === 'text' && dream.input.input) || (dream.input.inputType === 'audio' && dream.input.transcript) || (dream.input.inputType === 'image' && dream.input.description)}
</p>
{dream.input.inputType === 'image' && (
<div className="flex flex-col sm:flex-row gap-4 mb-4">
<div className="sm:w-1/3">
<img
src={`/assets/dreams/images/${dream.input.img}`}
alt={dream.input.imgAlt}
className="w-full rounded-lg shadow-lg object-cover"
/>
</div>
<div className="sm:w-2/3">
<p className="leading-relaxed text-base sm:text-lg">
{dream.input.description}
</p>
</div>
</div>
)}
{dream.input.inputType === 'audio' && (
<div className="flex flex-col gap-4 mb-4">
<div className="w-full">
<audio
controls
src={`/assets/dreams/audio/${dream.input.audio}`}
className="w-full"
>
Ihr Browser unterstützt das Audio-Element nicht.
</audio>
</div>
<div className="w-full">
<p className="leading-relaxed text-base sm:text-lg">
{dream.input.transcript}
</p>
</div>
</div>
)}
{dream.input.inputType === 'text' && (
<p className="leading-relaxed text-base sm:text-lg">
{dream.input.input}
</p>
)}
{dream.input.inputType === 'chip' && (
<div className="flex flex-col gap-4">
<p className="leading-relaxed text-base sm:text-lg">
{dream.input.text}
</p>
<Suspense fallback={<div
className="w-full h-64 mt-4 border border-gray-200 rounded-lg p-2 flex justify-center items-center">
<p>Lade EEG-Daten...</p>
</div>}>
<EEGChart chipInput={dream.input}/>
</Suspense>
<Suspense fallback={<div
className="w-full h-64 mt-4 border border-gray-200 rounded-lg p-2 flex justify-center items-center">
<p>Lade Vitaldaten...</p>
</div>}>
<VitalsChart chipInput={dream.input}/>
</Suspense>
<Suspense fallback={<div
className="w-full h-64 mt-4 border border-gray-200 rounded-lg p-2 flex justify-center items-center">
<p>Lade Bewegungsdaten...</p>
</div>}>
<MovementChart chipInput={dream.input}/>
</Suspense>
</div>
)}
</div>
{dream.ai?.interpretation && dream.ai.interpretation !== '' && (<div
@@ -91,33 +157,42 @@ export default function DreamPage() {
<div className="flex items-center mb-4">
<span className="font-medium dreamy-text">KI-Medien</span>
</div>
<Slider
dots={true}
infinite={true}
speed={500}
slidesToShow={1}
slidesToScroll={1}
className="max-w-full mx-auto"
>
<div className="flex justify-center px-2">
<img
src={`/assets/dreams/images/${dream.ai.image}`}
alt="KI-generiertes Traumbild"
className="max-w-full w-full rounded-lg shadow-lg object-contain mx-auto"
style={{maxHeight: '70vh'}}
/>
</div>
<div className="flex justify-center px-2">
<video
controls
src={`/assets/dreams/videos/${dream.ai.video}`}
className="max-w-full w-full rounded-lg shadow-lg object-contain mx-auto"
style={{maxHeight: '70vh'}}
>
Ihr Browser unterstützt das Video-Element nicht.
</video>
</div>
</Slider>
<Suspense fallback={<div className="flex justify-center items-center h-[70vh]">
<p>Lade Medien-Karussell...</p>
</div>}>
<SliderComponent
dots={true}
infinite={true}
speed={500}
slidesToShow={1}
slidesToScroll={1}
className="max-w-full mx-auto carousel-container"
onInit={() => {
import('slick-carousel/slick/slick.css');
import('slick-carousel/slick/slick-theme.css');
}}
>
<div className="flex justify-center px-2">
<img
src={`/assets/dreams/images/${dream.ai.image}`}
alt="KI-generiertes Traumbild"
className="max-w-full w-full rounded-lg shadow-lg object-contain mx-auto"
style={{maxHeight: '70vh'}}
/>
</div>
<div className="flex justify-center px-2">
<video
controls
loop
src={`/assets/dreams/videos/${dream.ai.video}`}
className="max-w-full w-full rounded-lg shadow-lg object-contain mx-auto"
style={{maxHeight: '70vh'}}
>
Ihr Browser unterstützt das Video-Element nicht.
</video>
</div>
</SliderComponent>
</Suspense>
</div>)}
{/* Show KI-Bild alone if video doesn't exist */}
@@ -145,6 +220,7 @@ export default function DreamPage() {
<div className="flex justify-center">
<video
controls
loop
src={`/assets/dreams/videos/${dream.ai.video}`}
className="max-w-full w-full rounded-lg shadow-lg object-contain mx-auto"
style={{maxHeight: '70vh'}}
@@ -172,6 +248,20 @@ export default function DreamPage() {
</div>
</div>)}
{/* VR Visualization */}
{(dream.id === 10 || dream.input.inputType === 'chip') && (
<div className="dreamy-card rounded-xl sm:rounded-2xl p-4 sm:p-6">
<div className="flex items-center mb-4">
<span className="font-medium dreamy-text">VR-Visualisierung</span>
</div>
<Suspense fallback={<div className="flex justify-center items-center h-[600px]">
<p>Lade VR-Visualisierung...</p>
</div>}>
<DreamVR dream={dream} height="600px"/>
</Suspense>
</div>
)}
<div className="dreamy-card rounded-xl sm:rounded-2xl p-4 sm:p-6">
<h2 className="text-lg font-semibold mb-3 dreamy-text">Details</h2>
<div className="space-y-3" style={{color: 'var(--text-muted)'}}>
@@ -183,7 +273,22 @@ export default function DreamPage() {
<span className="font-medium mb-1 sm:mb-0">Datum</span>
<span>
{formatDateNumeric(dream.date)}
</span>
</span>
</div>
<div className="flex flex-col sm:flex-row sm:justify-between">
<span className="font-medium mb-1 sm:mb-0">Tags</span>
<div className="flex flex-wrap gap-2">
{dream.tags.map((tag, index) => (
<span key={index} className="px-2 py-1 text-xs rounded-full"
style={{
background: 'var(--accent-gradient)',
color: 'white',
boxShadow: '0 2px 5px var(--shadow)'
}}>
{tag}
</span>
))}
</div>
</div>
</div>
</div>

View File

@@ -13,57 +13,60 @@ const ProfilePage: React.FC = () => {
return (
<div className="page p-4 pb-20">
{/* Profile Card */}
<div
className="dreamy-border rounded-xl p-6 max-w-md mx-auto overflow-hidden"
className="dreamy-border rounded-xl p-6 max-w-md mx-auto overflow-hidden mb-10"
style={{
background: 'linear-gradient(135deg, rgba(166, 77, 255, 0.25), rgba(213, 0, 249, 0.25))',
backdropFilter: 'blur(10px)',
boxShadow: '0 10px 30px var(--shadow), inset 0 0 15px rgba(166, 77, 255, 0.2)'
}}>
{/* Profile Picture */}
{/* Profile Picture */}
<div className="w-28 h-28 sm:w-36 sm:h-36 rounded-full overflow-hidden mb-6 mx-auto relative"
style={{
background: 'var(--accent-gradient)',
padding: '4px',
boxShadow: '0 0 15px var(--shadow)'
}}>
<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 rounded-full"
/>
</div>
{/* User Information */}
<div className="text-center w-full">
<h2 className="text-xl sm:text-2xl mb-2 break-words dreamy-text font-bold">{profileUser ? profileUser.name : defaultName}</h2>
<p className="text-text-muted mb-8 break-words">{profileUser ? profileUser.email : defaultEmail}</p>
<div className="grid grid-cols-2 gap-6 mt-6 text-center">
<div
className="dreamy-border rounded-lg p-4"
style={{
background: 'linear-gradient(135deg, rgba(166, 77, 255, 0.35), rgba(213, 0, 249, 0.35))',
boxShadow: '0 0 15px var(--shadow)',
backdropFilter: 'blur(5px)'
}}
>
<p className="dreamy-text text-2xl sm:text-3xl font-bold">{profileUser ? profileUser.dreamCount : defaultDreamCount}</p>
<p className="text-sm sm:text-base font-medium" style={{color: 'var(--text)'}}>Dreams</p>
<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 rounded-full"
/>
</div>
<div
className="dreamy-border rounded-lg p-4"
style={{
background: 'linear-gradient(135deg, rgba(166, 77, 255, 0.35), rgba(213, 0, 249, 0.35))',
boxShadow: '0 0 15px var(--shadow)',
backdropFilter: 'blur(5px)'
}}
>
<p className="dreamy-text text-2xl sm:text-3xl font-bold">{profileUser ? profileUser.streakDays : defaultStreakDays}</p>
<p className="text-sm sm:text-base font-medium" style={{color: 'var(--text)'}}>Days Streak</p>
</div>
</div>
{/* User Information */}
<div className="text-center w-full">
<h2 className="text-xl sm:text-2xl mb-2 break-words dreamy-text font-bold">{profileUser ? profileUser.name : defaultName}</h2>
<p className="text-text-muted mb-8 break-words">{profileUser ? profileUser.email : defaultEmail}</p>
<div className="grid grid-cols-2 gap-6 mt-6 text-center">
<div
className="dreamy-border rounded-lg p-4"
style={{
background: 'linear-gradient(135deg, rgba(166, 77, 255, 0.35), rgba(213, 0, 249, 0.35))',
boxShadow: '0 0 15px var(--shadow)',
backdropFilter: 'blur(5px)'
}}
>
<p className="dreamy-text text-2xl sm:text-3xl font-bold">{profileUser ? profileUser.dreamCount : defaultDreamCount}</p>
<p className="text-sm sm:text-base font-medium" style={{color: 'var(--text)'}}>Dreams</p>
</div>
<div
className="dreamy-border rounded-lg p-4"
style={{
background: 'linear-gradient(135deg, rgba(166, 77, 255, 0.35), rgba(213, 0, 249, 0.35))',
boxShadow: '0 0 15px var(--shadow)',
backdropFilter: 'blur(5px)'
}}
>
<p className="dreamy-text text-2xl sm:text-3xl font-bold">{profileUser ? profileUser.streakDays : defaultStreakDays}</p>
<p className="text-sm sm:text-base font-medium" style={{color: 'var(--text)'}}>Days Streak</p>
</div>
</div>
</div>
{/* Account Settings (moved back into profile card) */}
<div className="mt-10">
<h3 className="dreamy-text text-lg mb-4 font-bold">Account Settings</h3>
<div
@@ -84,8 +87,8 @@ const ProfilePage: React.FC = () => {
opacity: '0.9',
boxShadow: 'inset 0 0 5px rgba(166, 77, 255, 0.5)'
}}></span>
</label>
</div>
</label>
</div>
<div className="flex justify-between items-center py-3 flex-wrap">
<span className="mr-2 font-medium" style={{color: 'var(--text)'}}>Privacy</span>
<label className="relative inline-block w-12 h-6">
@@ -96,14 +99,58 @@ const ProfilePage: React.FC = () => {
opacity: '0.9',
boxShadow: 'inset 0 0 5px rgba(166, 77, 255, 0.5)'
}}></span>
</label>
</div>
</label>
</div>
</div>
</div>
</div>
{/* Project Information Card */}
<div
className="dreamy-border rounded-xl p-6 max-w-md mx-auto overflow-hidden"
style={{
background: 'linear-gradient(135deg, rgba(166, 77, 255, 0.25), rgba(213, 0, 249, 0.25))',
backdropFilter: 'blur(10px)',
boxShadow: '0 10px 30px var(--shadow), inset 0 0 15px rgba(166, 77, 255, 0.2)'
}}>
<h3 className="dreamy-text text-lg mb-4 font-bold text-center">Project Information</h3>
<div
className="dreamy-border rounded-lg p-5 text-left"
style={{
background: 'linear-gradient(135deg, rgba(166, 77, 255, 0.35), rgba(213, 0, 249, 0.35))',
boxShadow: '0 0 15px var(--shadow)',
backdropFilter: 'blur(5px)'
}}
>
<div className="py-2 border-b border-purple-100/30">
<p className="font-medium mb-1" style={{color: 'var(--text)'}}>Gitea Link:</p>
<a
href="https://gitea.puchstein.bayern/mpuchstein/REMind"
target="_blank"
rel="noopener noreferrer"
className="text-sm break-words hover:underline"
style={{color: 'var(--accent)'}}
>
https://gitea.puchstein.bayern/mpuchstein/REMind
</a>
</div>
<div className="py-2 border-b border-purple-100/30">
<p className="font-medium mb-1" style={{color: 'var(--text)'}}>Gruppenmitglieder:</p>
<p className="text-sm">Kim Anhäuser, Matthias Puchstein, Anya Zell</p>
</div>
<div className="py-2 border-b border-purple-100/30">
<p className="font-medium mb-1" style={{color: 'var(--text)'}}>Modul:</p>
<p className="text-sm">Einführung in die Text und Medienanalyse bei Prof. Dr. Rettiner und Dr. phil.
Ripoll</p>
</div>
<div className="py-2">
<p className="font-medium mb-1" style={{color: 'var(--text)'}}>Genutzte AI:</p>
<p className="text-sm">Perplexity, Junie, Veo, Imgen, Suno</p>
</div>
</div>
</div>
</div>
</div>
</div>
);
};
export default ProfilePage;
export default ProfilePage;

View File

@@ -1,10 +1,9 @@
import {FaCalendarAlt, FaClipboardCheck, FaGlobeAmericas} from 'react-icons/fa';
import {FaCalendarAlt, FaClipboardCheck, FaGlobeEurope, FaUsers} from 'react-icons/fa';
import mockResearchLive from '../../data/MockResearchLive';
import HeroSection from '../../components/dreamarchive/HeroSection';
import SectionHeader from '../../components/dreamarchive/SectionHeader';
import DreamyCard from '../../components/dreamarchive/DreamyCard';
import StudyCard from '../../components/dreamarchive/StudyCard';
import ResearcherInterviewCard from '../../components/dreamarchive/ResearcherInterviewCard';
import IconWithBackground from '../../components/dreamarchive/IconWithBackground';
export default function ResearchLive() {
@@ -36,34 +35,35 @@ export default function ResearchLive() {
case 'FaClipboardCheck':
IconComponent = FaClipboardCheck;
break;
case 'FaGlobeAmericas':
IconComponent = FaGlobeAmericas;
case 'FaGlobeEurope':
IconComponent = FaGlobeEurope;
break;
case 'FaCalendarAlt':
IconComponent = FaCalendarAlt;
case 'FaUsers':
IconComponent = FaUsers;
break;
default:
IconComponent = FaClipboardCheck;
}
return (<div key={project.id} className="bg-white/20 dark:bg-black/20 p-4 rounded-lg">
return (<div key={project.id}
className="bg-white/20 dark:bg-black/20 p-4 rounded-lg flex flex-col min-h-[200px]">
<div className="flex justify-center mb-3">
<div className="p-2 bg-violet-500/20 rounded-full">
<IconComponent className="text-violet-600 dark:text-violet-400" size={20}/>
</div>
</div>
<h4 className="font-bold mb-2 text-center">{project.title}</h4>
<p className="text-sm">
<p className="text-sm flex-grow">
{project.description}
</p>
<div className="mt-3 text-center">
{project.optedIn ? (
<button
className="py-1 px-3 bg-violet-500 hover:bg-violet-600 text-white text-xs rounded-lg transition-colors">
Du machst bereits mit!
className="py-1 px-3 text-white text-xs rounded-lg transition-colors">
Danke!
</button>) : (
<button
className="py-1 px-3 bg-violet-500 hover:bg-violet-600 text-green text-xs rounded-lg transition-colors">
className="py-1 px-3 dreamy-button text-green text-xs rounded-lg transition-colors">
Beitragen
</button>
)}
@@ -94,21 +94,6 @@ export default function ResearchLive() {
</div>
</div>
Researcher Interviews
<div className="mb-12">
<SectionHeader title="Forscher-Interviews"/>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{mockResearchLive.researcherInterviews.slice(0, 2).map((interview) => (
<ResearcherInterviewCard key={interview.id} interview={interview}/>))}
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mt-6">
{mockResearchLive.researcherInterviews.slice(2, 4).map((interview) => (
<ResearcherInterviewCard key={interview.id} interview={interview}/>))}
</div>
</div>
{/* Upcoming Events */}
<div className="mb-12">
<SectionHeader title="Kommende Veranstaltungen"/>

View File

@@ -4,6 +4,240 @@ import HeroSection from '../../components/dreamarchive/HeroSection';
import SectionHeader from '../../components/dreamarchive/SectionHeader';
import DreamyCard from '../../components/dreamarchive/DreamyCard';
import IconWithBackground from '../../components/dreamarchive/IconWithBackground';
import {useEffect, useRef} from 'react';
import * as d3 from 'd3';
// Dream types data with percentages and colors
const dreamTypesData = [
{type: "Angstträume", percentage: 34, color: "#ef4444", darkColor: "#dc2626", note: "Global durchschnittlich"},
{type: "Soziale Träume", percentage: 28, color: "#3b82f6", darkColor: "#2563eb", note: "Kulturelle Variationen"},
{type: "Flugträume", percentage: 18, color: "#a855f7", darkColor: "#9333ea", note: "Persönlichkeitsabhängig"},
{type: "Sexuelle Träume", percentage: 12, color: "#ec4899", darkColor: "#db2777", note: "Altersabhängig"},
{type: "Verfolgungsträume", percentage: 8, color: "#f97316", darkColor: "#ea580c", note: "Stresskorreliert"}
];
// D3.js Pie Chart Component
function D3PieChart() {
const svgRef = useRef<SVGSVGElement>(null);
const tooltipRef = useRef<HTMLDivElement>(null);
const containerRef = useRef<HTMLDivElement>(null);
const isDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
useEffect(() => {
if (!svgRef.current || !tooltipRef.current || !containerRef.current) return;
// Clear any existing SVG content
d3.select(svgRef.current).selectAll("*").remove();
// Set up dimensions
const width = svgRef.current.clientWidth;
const height = svgRef.current.clientHeight;
const margin = 10;
const radius = Math.min(width, height) / 2 - margin;
// Create SVG
const svg = d3.select(svgRef.current)
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", `translate(${width / 2}, ${height / 2})`);
// Create pie generator
const pie = d3.pie<typeof dreamTypesData[0]>()
.value(d => d.percentage)
.sort(null);
// Create arc generator
const arc = d3.arc<d3.PieArcDatum<typeof dreamTypesData[0]>>()
.innerRadius(0)
.outerRadius(radius);
// Get tooltip element
const tooltip = d3.select(tooltipRef.current);
// Function to show tooltip
const showTooltip = (event: MouseEvent | TouchEvent, d: d3.PieArcDatum<typeof dreamTypesData[0]>) => {
const data = d.data;
// Set tooltip content
tooltip.html(`
<div class="font-bold">${data.type}</div>
<div>${data.percentage}%</div>
<div class="text-xs opacity-75">${data.note}</div>
`);
// Make tooltip visible
tooltip
.style("opacity", 1)
.style("visibility", "visible");
// Position tooltip
const containerRect = containerRef.current!.getBoundingClientRect();
let x, y;
if (event instanceof MouseEvent) {
x = event.clientX - containerRect.left;
y = event.clientY - containerRect.top;
} else {
// TouchEvent
const touch = (event as TouchEvent).touches[0];
x = touch.clientX - containerRect.left;
y = touch.clientY - containerRect.top;
}
// Adjust position to avoid going off the container
const tooltipRect = tooltipRef.current!.getBoundingClientRect();
if (x + tooltipRect.width > containerRect.width) {
x = x - tooltipRect.width;
}
if (y + tooltipRect.height > containerRect.height) {
y = y - tooltipRect.height;
}
tooltip
.style("left", `${x + 10}px`)
.style("top", `${y + 10}px`);
};
// Function to hide tooltip
const hideTooltip = () => {
tooltip
.style("opacity", 0)
.style("visibility", "hidden");
};
// Create pie chart with event handlers
svg.selectAll("path")
.data(pie(dreamTypesData))
.enter()
.append("path")
.attr("d", arc)
.attr("fill", d => isDarkMode ? d.data.darkColor : d.data.color)
.attr("stroke", "white")
.style("stroke-width", "1px")
.style("cursor", "pointer")
// Desktop hover events
.on("mouseover", function (event, d) {
// Calculate centroid for this arc to ensure zoom is centered properly
const centroid = arc.centroid(d);
const x = centroid[0];
const y = centroid[1];
d3.select(this).transition().duration(200)
.attr("opacity", 0.8)
.attr("stroke-width", "2px")
// Apply zoom transformation centered on the arc's centroid
.attr("transform", `translate(${x},${y}) scale(1.05) translate(${-x},${-y})`);
showTooltip(event, d);
})
.on("mouseout", function () {
d3.select(this).transition().duration(200)
.attr("opacity", 1)
.attr("stroke-width", "1px")
.attr("transform", null); // Completely remove the transform to reset zoom
hideTooltip();
})
// Mobile/touch events
.on("touchstart", function (event, d) {
// Stop propagation to prevent document touchstart from firing
event.stopPropagation();
// Prevent default to avoid any browser handling
event.preventDefault();
// Store a reference to the element without using 'this'
const element = event.currentTarget;
// Calculate centroid for this arc to ensure zoom is centered properly
const centroid = arc.centroid(d);
const x = centroid[0];
const y = centroid[1];
// Visual feedback
d3.select(element).transition().duration(200)
.attr("opacity", 0.8)
.attr("stroke-width", "2px")
// Apply zoom transformation centered on the arc's centroid
.attr("transform", `translate(${x},${y}) scale(1.05) translate(${-x},${-y})`);
// Show tooltip
showTooltip(event, d);
// Add a temporary touchend handler to reset the visual state
// but keep the tooltip visible
document.addEventListener("touchend", function () {
d3.select(element).transition().duration(200)
.attr("opacity", 1)
.attr("stroke-width", "1px")
.attr("transform", null); // Completely remove the transform to reset zoom
}, {once: true});
})
.on("click", function (event, d) {
event.stopPropagation();
showTooltip(event, d);
});
// Add center circle
svg.append("circle")
.attr("cx", 0)
.attr("cy", 0)
.attr("r", radius * 0.4)
.attr("fill", isDarkMode ? "#1f2937" : "#ffffff")
.attr("stroke", isDarkMode ? "#374151" : "#e5e7eb")
.style("stroke-width", "1px");
// Add center text
svg.append("text")
.attr("text-anchor", "middle")
.attr("dy", "0.35em")
.attr("class", "text-sm font-bold")
.attr("fill", isDarkMode ? "#e5e7eb" : "#1f2937")
.text("100%");
// Create a function to handle document touchstart that doesn't immediately hide the tooltip
const handleDocumentTouchStart = (event: TouchEvent) => {
// Only hide the tooltip if the touch is outside the pie chart segments
const paths = svg.selectAll("path").nodes();
const touchTarget = document.elementFromPoint(
event.touches[0].clientX,
event.touches[0].clientY
);
// Don't hide if touching a pie segment
if (paths.some(path => path === touchTarget)) {
return;
}
hideTooltip();
};
// Add click event to document to hide tooltip when clicking outside
document.addEventListener("click", hideTooltip);
document.addEventListener("touchstart", handleDocumentTouchStart);
// Cleanup event listeners on unmount
return () => {
document.removeEventListener("click", hideTooltip);
document.removeEventListener("touchstart", handleDocumentTouchStart);
};
}, [isDarkMode]);
return (
<div ref={containerRef} className="relative w-full aspect-square max-w-xs mx-auto">
<svg ref={svgRef} className="w-full h-full"></svg>
<div
ref={tooltipRef}
className="absolute pointer-events-none bg-white dark:bg-gray-800 p-2 rounded shadow-lg text-sm z-10 transition-opacity duration-200"
style={{
opacity: 0,
visibility: 'hidden',
maxWidth: '150px',
border: '1px solid',
borderColor: isDarkMode ? 'rgba(75, 85, 99, 0.5)' : 'rgba(229, 231, 235, 0.5)'
}}
></div>
</div>
);
}
export default function Technology() {
return (<div className="p-4 pt-24 pb-20 max-w-6xl mx-auto relative overflow-hidden">
@@ -116,42 +350,8 @@ export default function Technology() {
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
<div>
{/* Pie Chart Visualization (Mock) */}
<div className="relative w-full aspect-square max-w-xs mx-auto">
<div className="absolute inset-0 rounded-full bg-gray-200 dark:bg-gray-700"></div>
{/* Angstträume: 34% */}
<div className="absolute inset-0 rounded-full bg-red-500 dark:bg-red-600" style={{
clipPath: 'polygon(50% 50%, 50% 0%, 100% 0%, 100% 50%, 75% 75%)'
}}></div>
{/* Soziale Träume: 28% */}
<div className="absolute inset-0 rounded-full bg-blue-500 dark:bg-blue-600" style={{
clipPath: 'polygon(50% 50%, 75% 75%, 50% 100%, 0% 100%, 0% 50%)'
}}></div>
{/* Sexuelle Träume: 12% */}
<div className="absolute inset-0 rounded-full bg-pink-500 dark:bg-pink-600" style={{
clipPath: 'polygon(50% 50%, 0% 50%, 0% 0%, 25% 0%)'
}}></div>
{/* Flugträume: 18% */}
<div className="absolute inset-0 rounded-full bg-purple-500 dark:bg-purple-600" style={{
clipPath: 'polygon(50% 50%, 25% 0%, 50% 0%)'
}}></div>
{/* Verfolgungsträume: 8% */}
<div className="absolute inset-0 rounded-full bg-orange-500 dark:bg-orange-600" style={{
clipPath: 'polygon(50% 50%, 50% 100%, 25% 100%)'
}}></div>
<div className="absolute inset-0 flex items-center justify-center">
<div
className="w-16 h-16 rounded-full bg-white dark:bg-gray-900 flex items-center justify-center text-sm font-bold">
100%
</div>
</div>
</div>
{/* D3.js Pie Chart */}
<D3PieChart/>
</div>
<div>
@@ -192,23 +392,6 @@ export default function Technology() {
</div>
</div>
<div className="flex items-center">
<div className="w-4 h-4 bg-pink-500 dark:bg-pink-600 mr-2"></div>
<div className="flex-1">
<div className="flex justify-between">
<span className="text-sm font-bold">Sexuelle Träume</span>
<span className="text-sm">12%</span>
</div>
<div
className="relative h-2 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden">
<div
className="absolute top-0 left-0 h-full bg-pink-500 dark:bg-pink-600 rounded-full"
style={{width: '12%'}}></div>
</div>
<p className="text-xs mt-1" style={getTextStyle('muted')}>Altersabhängig</p>
</div>
</div>
<div className="flex items-center">
<div className="w-4 h-4 bg-purple-500 dark:bg-purple-600 mr-2"></div>
<div className="flex-1">
@@ -227,6 +410,23 @@ export default function Technology() {
</div>
</div>
<div className="flex items-center">
<div className="w-4 h-4 bg-pink-500 dark:bg-pink-600 mr-2"></div>
<div className="flex-1">
<div className="flex justify-between">
<span className="text-sm font-bold">Sexuelle Träume</span>
<span className="text-sm">12%</span>
</div>
<div
className="relative h-2 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden">
<div
className="absolute top-0 left-0 h-full bg-pink-500 dark:bg-pink-600 rounded-full"
style={{width: '12%'}}></div>
</div>
<p className="text-xs mt-1" style={getTextStyle('muted')}>Altersabhängig</p>
</div>
</div>
<div className="flex items-center">
<div className="w-4 h-4 bg-orange-500 dark:bg-orange-600 mr-2"></div>
<div className="flex-1">

View File

@@ -18,7 +18,16 @@ export type AudioInput = {
export type ChipInput = {
inputType: "chip";
chip: string;
text: string;
eeg: {
alpha: number[];
beta: number[];
theta: number[];
delta: number[];
}
puls: number[];
hrv: number[];
bewegung: number[];
}
export default class Dream{
@@ -26,6 +35,7 @@ export default class Dream{
userId: number;
title: string;
date: Date;
tags: string[];
input: TextInput | ImageInput | AudioInput | ChipInput;
ai?:{
interpretation: string;
@@ -34,13 +44,17 @@ export default class Dream{
video?: string;
models?: string;
}
vr?: {
models: never;
}
constructor(params: {
id: number;
userId: number;
title: string;
date: Date;
input: TextInput | ImageInput | AudioInput;
tags: string[];
input: TextInput | ImageInput | AudioInput | ChipInput;
ai: {
interpretation: string;
image?: string;
@@ -53,6 +67,7 @@ export default class Dream{
this.userId = params.userId;
this.title = params.title;
this.date = params.date;
this.tags = params.tags;
this.input = params.input;
this.ai= params.ai;
}

View File

@@ -8,4 +8,34 @@ export default defineConfig({
react(),
tailwindcss()
],
build: {
rollupOptions: {
output: {
manualChunks: {
// Group Three.js related libraries
'three-bundle': [
'three',
'@react-three/fiber',
'@react-three/drei',
'three-stdlib'
],
// D3 visualization library
'd3': ['d3'],
// Slider related libraries
'slider': [
'react-slick',
'slick-carousel'
],
// React and related libraries
'react-vendor': [
'react',
'react-dom',
'react-router-dom'
]
}
}
},
// Increase the warning limit to avoid unnecessary warnings
chunkSizeWarningLimit: 1000
}
})