refactoring

This commit is contained in:
Sascha Bach 2025-11-26 13:19:59 +01:00
parent d99c0e2429
commit 2169c2994b
6 changed files with 119 additions and 55 deletions

View File

@ -0,0 +1,58 @@
import type { Service } from '../data/Service';
interface FeatureCardsGridProps {
featureCards: Service[];
}
export default function FeatureCardsGrid({ featureCards }: FeatureCardsGridProps) {
return (
<div
id="features"
className="about-section__features"
aria-labelledby="features-heading"
>
<h3 id="features-heading" className="sr-only">My Areas of Expertise</h3>
<div className="about-section__features-grid">
{featureCards.map((card, index) => {
const IconComponent = card.icon;
const cardId = card.title.toLowerCase().replaceAll(/\s+/g, '-');
return (
<article
key={cardId}
className={`about-section__feature-card about-section__feature-card--${card.colorClass}`}
aria-labelledby={`feature-${cardId}-title`}
aria-describedby={`feature-${cardId}-description`}
>
<header className="about-section__feature-header">
<IconComponent
className={`about-section__feature-icon about-section__feature-icon--${card.colorClass}`}
aria-hidden="true"
/>
<h4
id={`feature-${cardId}-title`}
className={`about-section__feature-title about-section__feature-title--${card.colorClass}`}
>
{card.title}
</h4>
</header>
<div className="about-section__feature-content">
<p
id={`feature-${cardId}-description`}
className={`about-section__feature-description about-section__feature-description--${card.colorClass}`}
>
{card.description}
</p>
</div>
{/* Status indicator for screen readers */}
<div className="sr-only">
Expertise area {index + 1} of {featureCards.length}
</div>
</article>
);
})}
</div>
</div>
);
}

View File

@ -1,5 +1,6 @@
import { Code, Palette, ShieldCheck, Eye } from 'lucide-react';
import { useLanguage } from '../../contexts/LanguageContext';
import FeatureCardsGrid from '../FeatureCardsGrid';
import type { Service } from '../../data/Service';
import saschaImage from '../../assets/sascha.png';
@ -28,7 +29,7 @@ export default function AboutSection(props: AboutSectionProps = {}) {
icon: [Code, Palette, ShieldCheck, Eye][index] || Code,
title: card.title,
description: card.description,
colorClass: ["primary", "secondary", "tertiary", "quaternary"][index] || "primary"
colorClass: (["primary", "secondary", "tertiary", "quaternary"][index] || "primary") as "primary" | "secondary" | "tertiary" | "quaternary"
}))
} = props;
@ -79,54 +80,7 @@ export default function AboutSection(props: AboutSectionProps = {}) {
</main>
{/* Feature Cards Grid */}
<div
id="features"
className="about-section__features"
aria-labelledby="features-heading"
>
<h3 id="features-heading" className="sr-only">My Areas of Expertise</h3>
<div className="about-section__features-grid">
{featureCards.map((card, index) => {
const IconComponent = card.icon;
const cardId = card.title.toLowerCase().replaceAll(/\s+/g, '-');
return (
<article
key={cardId}
className={`about-section__feature-card about-section__feature-card--${card.colorClass}`}
aria-labelledby={`feature-${cardId}-title`}
aria-describedby={`feature-${cardId}-description`}
>
<header className="about-section__feature-header">
<IconComponent
className={`about-section__feature-icon about-section__feature-icon--${card.colorClass}`}
aria-hidden="true"
/>
<h4
id={`feature-${cardId}-title`}
className={`about-section__feature-title about-section__feature-title--${card.colorClass}`}
>
{card.title}
</h4>
</header>
<div className="about-section__feature-content">
<p
id={`feature-${cardId}-description`}
className={`about-section__feature-description about-section__feature-description--${card.colorClass}`}
>
{card.description}
</p>
</div>
{/* Status indicator for screen readers */}
<div className="sr-only">
Expertise area {index + 1} of {featureCards.length}
</div>
</article>
);
})}
</div>
</div>
<FeatureCardsGrid featureCards={featureCards} />
</div>
</section >
);

View File

@ -1,5 +1,6 @@
import { Award } from 'lucide-react';
import { useLanguage } from '../../contexts/LanguageContext';
import { certificationsData } from '../../config/certifications-config';
import type { Certification } from '../../data/Certification';
interface CertificationsSectionProps {
@ -17,12 +18,7 @@ export default function CertificationsSection(props: CertificationsSectionProps
const {
title = texts.title,
subtitle = texts.subtitle,
certifications = texts.certificationItems.map((cert, index) => ({
name: cert.name,
issuer: cert.issuer,
year: ["2025", "2025", "2020", "2020", "2015"][index] || "2025",
icon: ""
}))
certifications = certificationsData
} = props;
// Sort certifications by year in descending order (newest first)

View File

@ -0,0 +1,46 @@
import { certifications } from './locales/en/certifications';
import type { Certification } from '../data/Certification';
const getCertificationText = (id: number) => {
const item = certifications.certificationItems.find(c => c.id === id);
if (!item) throw new Error(`Certification with id ${id} not found`);
return item;
};
export const certificationsData: Certification[] = [
{
id: 1,
name: getCertificationText(1).name,
issuer: getCertificationText(1).issuer,
year: '2025',
icon: '',
},
{
id: 2,
name: getCertificationText(2).name,
issuer: getCertificationText(2).issuer,
year: '2025',
icon: '',
},
{
id: 3,
name: getCertificationText(3).name,
issuer: getCertificationText(3).issuer,
year: '2020',
icon: '',
},
{
id: 4,
name: getCertificationText(4).name,
issuer: getCertificationText(4).issuer,
year: '2020',
icon: '',
},
{
id: 5,
name: getCertificationText(5).name,
issuer: getCertificationText(5).issuer,
year: '2015',
icon: '',
},
].sort((a, b) => Number.parseInt(b.year) - Number.parseInt(a.year));

View File

@ -3,22 +3,27 @@ export const certifications = {
subtitle: 'Meine beruflichen Qualifikationen und kontinuierliche Weiterbildung',
certificationItems: [
{
id: 1,
name: 'Practical Prompt Engineering Masterclass: Hands-On Learning',
issuer: 'Udemy Kurs - Asif Farooqi, Abdullah Dar',
},
{
id: 2,
name: 'Understanding Typescript',
issuer: 'Udemy Kurs - Maximilian Schwarzmüller',
},
{
id: 3,
name: 'Angular Step by Step',
issuer: 'Udemy Kurs - Shivprasad Koirala',
},
{
id: 4,
name: 'Angular and Typescript',
issuer: 'LinkedIn TestDome',
},
{
id: 5,
name: 'Bachelor of Science in Informatik',
issuer: 'TU Darmstadt',
},

View File

@ -3,22 +3,27 @@ export const certifications = {
subtitle: 'My professional qualifications and continuous learning',
certificationItems: [
{
id: 1,
name: 'Practical Prompt Engineering Masterclass: Hands-On Learning',
issuer: 'Udemy Lecture - Asif Farooqi, Abdullah Dar',
},
{
id: 2,
name: 'Understanding Typescript',
issuer: 'Udemy Lecture - Maximilian Schwarzmüller',
},
{
id: 3,
name: 'Angular Step by Step',
issuer: 'Udemy Lecture - Shivprasad Koirala',
},
{
id: 4,
name: 'Angular and Typescript',
issuer: 'LinkedIn TestDome',
},
{
id: 5,
name: 'Bachelor of Science in Computer Science',
issuer: 'TU Darmstadt',
},