From e10d46b5a17692983b8f0708505e75b461dd8279 Mon Sep 17 00:00:00 2001 From: Matthias Puchstein Date: Sat, 12 Jul 2025 14:06:48 +0200 Subject: [PATCH] modularized `ResearchLive` by introducing reusable components (`DreamyCard`, `HeroSection`, `SectionHeader`, etc.), reducing code duplication and improving maintainability Signed-off-by: Matthias Puchstein --- src/components/dreamarchive/DreamyCard.tsx | 27 +++++++ src/components/dreamarchive/HeroSection.tsx | 31 ++++++++ .../dreamarchive/IconWithBackground.tsx | 26 +++++++ .../dreamarchive/NavigationLinks.tsx | 32 +++++++++ .../dreamarchive/ResearcherInterviewCard.tsx | 48 +++++++++++++ src/components/dreamarchive/SectionHeader.tsx | 14 ++++ src/components/dreamarchive/StudyCard.tsx | 71 +++++++++++++++++++ src/components/dreamarchive/VideoPlayer.tsx | 25 +++++++ 8 files changed, 274 insertions(+) create mode 100644 src/components/dreamarchive/DreamyCard.tsx create mode 100644 src/components/dreamarchive/HeroSection.tsx create mode 100644 src/components/dreamarchive/IconWithBackground.tsx create mode 100644 src/components/dreamarchive/NavigationLinks.tsx create mode 100644 src/components/dreamarchive/ResearcherInterviewCard.tsx create mode 100644 src/components/dreamarchive/SectionHeader.tsx create mode 100644 src/components/dreamarchive/StudyCard.tsx create mode 100644 src/components/dreamarchive/VideoPlayer.tsx diff --git a/src/components/dreamarchive/DreamyCard.tsx b/src/components/dreamarchive/DreamyCard.tsx new file mode 100644 index 0000000..8ce176f --- /dev/null +++ b/src/components/dreamarchive/DreamyCard.tsx @@ -0,0 +1,27 @@ +import React, {ReactNode} from 'react'; +import {getBackgroundStyle} from '../../styles/StyleUtils'; + +interface DreamyCardProps { + children: ReactNode; + color?: 'purple' | 'blue' | 'violet' | 'emerald' | 'amber' | 'rose' | 'pink-red' | 'cta'; + className?: string; + padding?: string; +} + +export const DreamyCard: React.FC = ({ + children, + color = 'purple', + className = '', + padding = 'p-6' + }) => { + return ( +
+ {children} +
+ ); +}; + +export default DreamyCard; \ No newline at end of file diff --git a/src/components/dreamarchive/HeroSection.tsx b/src/components/dreamarchive/HeroSection.tsx new file mode 100644 index 0000000..f62d651 --- /dev/null +++ b/src/components/dreamarchive/HeroSection.tsx @@ -0,0 +1,31 @@ +import React from 'react'; + +interface HeroSectionProps { + title: string; + subtitle: string; + containerTitle: string; + description: string; +} + +export const HeroSection: React.FC = ({title, subtitle, containerTitle, description}) => { + return ( +
+
+

{title}

+

{subtitle}

+
+
+
+

{containerTitle}

+
+

+ {description} +

+
+
+ ); +}; + +export default HeroSection; diff --git a/src/components/dreamarchive/IconWithBackground.tsx b/src/components/dreamarchive/IconWithBackground.tsx new file mode 100644 index 0000000..14f36db --- /dev/null +++ b/src/components/dreamarchive/IconWithBackground.tsx @@ -0,0 +1,26 @@ +import React, {ReactElement} from 'react'; + +interface IconWithBackgroundProps { + icon: ReactElement; + color: string; + size?: number; + className?: string; +} + +export const IconWithBackground: React.FC = ({ + icon, + color, + size = 28, + className = '' + }) => { + return ( +
+ {React.cloneElement(icon, { + className: `text-${color}-600 dark:text-${color}-400`, + size: size + })} +
+ ); +}; + +export default IconWithBackground; diff --git a/src/components/dreamarchive/NavigationLinks.tsx b/src/components/dreamarchive/NavigationLinks.tsx new file mode 100644 index 0000000..5b6edbe --- /dev/null +++ b/src/components/dreamarchive/NavigationLinks.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import {NavLink} from 'react-router-dom'; + +interface NavigationLinksProps { + previousLink?: { + to: string; + text: string; + }; + nextLink?: { + to: string; + text: string; + }; +} + +export const NavigationLinks: React.FC = ({previousLink, nextLink}) => { + return ( +
+ {previousLink && ( + + {previousLink.text} + + )} + {nextLink && ( + + {nextLink.text} + + )} +
+ ); +}; + +export default NavigationLinks; \ No newline at end of file diff --git a/src/components/dreamarchive/ResearcherInterviewCard.tsx b/src/components/dreamarchive/ResearcherInterviewCard.tsx new file mode 100644 index 0000000..f291843 --- /dev/null +++ b/src/components/dreamarchive/ResearcherInterviewCard.tsx @@ -0,0 +1,48 @@ +import React from 'react'; +import {FaUserMd} from 'react-icons/fa'; +import {getBackgroundStyle, getTextStyle} from '../../styles/StyleUtils'; +import IconWithBackground from './IconWithBackground'; +import VideoPlayer from './VideoPlayer'; + +interface ResearcherInterviewCardProps { + interview: { + id: number; + name: string; + institution: string; + specialty: string; + topics: string[]; + color: string; + videoId: string; + }; +} + +export const ResearcherInterviewCard: React.FC = ({interview}) => { + return ( +
+
+ } + color={interview.color} + size={28} + className="mr-4 mt-1" + /> +
+

{interview.name}

+

{interview.institution}

+

{interview.specialty}

+ + + +

Themen im Interview:

+
    + {interview.topics.map((topic, index) => ( +
  • {topic}
  • + ))} +
+
+
+
+ ); +}; + +export default ResearcherInterviewCard; \ No newline at end of file diff --git a/src/components/dreamarchive/SectionHeader.tsx b/src/components/dreamarchive/SectionHeader.tsx new file mode 100644 index 0000000..acbc070 --- /dev/null +++ b/src/components/dreamarchive/SectionHeader.tsx @@ -0,0 +1,14 @@ +import React from 'react'; + +interface SectionHeaderProps { + title: string; + className?: string; +} + +export const SectionHeader: React.FC = ({title, className = ''}) => { + return ( +

{title}

+ ); +}; + +export default SectionHeader; \ No newline at end of file diff --git a/src/components/dreamarchive/StudyCard.tsx b/src/components/dreamarchive/StudyCard.tsx new file mode 100644 index 0000000..ff6ecbc --- /dev/null +++ b/src/components/dreamarchive/StudyCard.tsx @@ -0,0 +1,71 @@ +import React from 'react'; +import {FaFlask} from 'react-icons/fa'; +import {getBackgroundStyle, getTextStyle} from '../../styles/StyleUtils'; +import IconWithBackground from './IconWithBackground'; + +interface StudyCardProps { + study: { + id: number; + title: string; + institution: string; + status: string; + statusColor: string; + participants: { + current: number; + target: number; + }; + endDate: string; + description: string; + color: string; + }; +} + +export const StudyCard: React.FC = ({study}) => { + return ( +
+
+ } + color={study.color} + size={28} + className="mr-4" + /> +
+

{study.title}

+

{study.institution}

+
+
+ +
+
+ Status: + + {study.status} + +
+
+ Teilnehmer: + + {study.participants.current.toLocaleString()} / {study.participants.target.toLocaleString()} + +
+
+ Enddatum: + {study.endDate} +
+
+ +

+ {study.description} +

+ + +
+ ); +}; + +export default StudyCard; \ No newline at end of file diff --git a/src/components/dreamarchive/VideoPlayer.tsx b/src/components/dreamarchive/VideoPlayer.tsx new file mode 100644 index 0000000..a3d22e0 --- /dev/null +++ b/src/components/dreamarchive/VideoPlayer.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import {FaVideo} from 'react-icons/fa'; + +interface VideoPlayerProps { + color: string; +} + +export const VideoPlayer: React.FC = ({color}) => { + return ( +
+
+ +
+
+
+
+
+
+
+ ); +}; + +export default VideoPlayer; \ No newline at end of file