Implementierung - SEO - 01.08.2025

This commit is contained in:
2025-08-01 14:26:05 +02:00
parent 7aa0543001
commit f176743885
35 changed files with 3427 additions and 98 deletions
+160
View File
@@ -0,0 +1,160 @@
import React, { useState } from 'react';
import { ChevronDown, ChevronUp } from 'lucide-react';
import { useTranslation } from 'react-i18next';
interface FAQItem {
question: string;
answer: string;
category?: string;
}
export function FAQSection() {
const { t, i18n } = useTranslation();
const [openItems, setOpenItems] = useState<number[]>([]);
const faqs: FAQItem[] = i18n.language === 'de' ? [
{
question: "Wie kann ich Python für Prozessautomatisierung einsetzen?",
answer: "Python eignet sich hervorragend für Prozessautomatisierung durch Libraries wie Selenium für Web-Automation, Pandas für Datenverarbeitung und Schedule für zeitgesteuerte Aufgaben. Ich entwickle maßgeschneiderte Automatisierungslösungen für Ihre Geschäftsprozesse.",
category: "python"
},
{
question: "Was macht OLLAMA ideal für lokale KI-Entwicklung?",
answer: "OLLAMA ermöglicht das Ausführen großer Sprachmodelle lokal, was Datenschutz gewährleistet und API-Kosten reduziert. Ich spezialisiere mich auf die Integration von OLLAMA in Anwendungen für individuelle KI-Lösungen ohne Cloud-Abhängigkeiten.",
category: "ai"
},
{
question: "Welche Vorteile bieten Electron Desktop Apps?",
answer: "Electron ermöglicht die Entwicklung plattformübergreifender Desktop-Anwendungen mit Web-Technologien. Sie erhalten eine native App für Windows, Mac und Linux aus einer einzigen Codebasis. Ideal für Tools mit komplexer UI und Offline-Funktionalität.",
category: "electron"
},
{
question: "Wie lange dauert die Entwicklung einer Web-Anwendung?",
answer: "Die Entwicklungsdauer hängt vom Umfang ab. Ein MVP kann in 4-8 Wochen entwickelt werden, während komplexe Enterprise-Anwendungen 3-6 Monate benötigen. Ich arbeite agil und liefere in 2-Wochen-Sprints nutzbare Zwischenergebnisse.",
category: "general"
},
{
question: "Können Sie bestehende Python-Skripte in Web-Apps umwandeln?",
answer: "Ja, ich kann Ihre Python-Skripte in moderne Web-Anwendungen mit FastAPI oder Django Backend und React Frontend transformieren. Dies ermöglicht Multi-User-Zugriff, bessere UI/UX und zentrale Datenverwaltung.",
category: "python"
},
{
question: "Wie integrieren Sie KI in bestehende Geschäftsprozesse?",
answer: "Ich analysiere Ihre Workflows und identifiziere Automatisierungspotenziale. Mit OLLAMA oder anderen KI-Modellen entwickle ich Lösungen für Textanalyse, Dokumentenverarbeitung, Kundenservice-Automation oder Datenextraktion - alles datenschutzkonform on-premise.",
category: "ai"
}
] : [
{
question: "How can I use Python for process automation?",
answer: "Python excels at process automation through libraries like Selenium for web automation, Pandas for data processing, and Schedule for timed tasks. I develop custom automation solutions tailored to your business processes.",
category: "python"
},
{
question: "What makes OLLAMA ideal for local AI development?",
answer: "OLLAMA enables running large language models locally, ensuring data privacy and reducing API costs. I specialize in integrating OLLAMA into applications for custom AI solutions without cloud dependencies.",
category: "ai"
},
{
question: "What are the advantages of Electron desktop apps?",
answer: "Electron enables cross-platform desktop app development using web technologies. You get a native app for Windows, Mac, and Linux from a single codebase. Ideal for tools requiring complex UI and offline functionality.",
category: "electron"
},
{
question: "How long does web application development take?",
answer: "Development time depends on scope. An MVP can be developed in 4-8 weeks, while complex enterprise applications require 3-6 months. I work agile and deliver usable results in 2-week sprints.",
category: "general"
},
{
question: "Can you convert existing Python scripts into web apps?",
answer: "Yes, I can transform your Python scripts into modern web applications with FastAPI or Django backend and React frontend. This enables multi-user access, better UI/UX, and centralized data management.",
category: "python"
},
{
question: "How do you integrate AI into existing business processes?",
answer: "I analyze your workflows and identify automation potential. Using OLLAMA or other AI models, I develop solutions for text analysis, document processing, customer service automation, or data extraction - all privacy-compliant on-premise.",
category: "ai"
}
];
const toggleItem = (index: number) => {
setOpenItems(prev =>
prev.includes(index)
? prev.filter(i => i !== index)
: [...prev, index]
);
};
const faqSchema = {
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": faqs.map(faq => ({
"@type": "Question",
"name": faq.question,
"acceptedAnswer": {
"@type": "Answer",
"text": faq.answer
}
}))
};
return (
<section className="faq-section py-16 px-4 max-w-4xl mx-auto">
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(faqSchema) }}
/>
<h2 className="text-3xl font-bold text-white mb-8 text-center">
{t('faq.title', 'Frequently Asked Questions')}
</h2>
<div className="space-y-4">
{faqs.map((faq, index) => (
<div
key={index}
className="faq-item bg-zinc-800/50 border border-zinc-700 rounded-lg overflow-hidden"
>
<button
onClick={() => toggleItem(index)}
className="w-full px-6 py-4 text-left flex items-center justify-between hover:bg-zinc-800/70 transition-colors"
aria-expanded={openItems.includes(index)}
aria-controls={`faq-answer-${index}`}
>
<h3 className="text-lg font-medium text-white pr-4">
{faq.question}
</h3>
{openItems.includes(index) ? (
<ChevronUp className="h-5 w-5 text-zinc-400 flex-shrink-0" />
) : (
<ChevronDown className="h-5 w-5 text-zinc-400 flex-shrink-0" />
)}
</button>
<div
id={`faq-answer-${index}`}
className={`overflow-hidden transition-all duration-300 ${
openItems.includes(index) ? 'max-h-96' : 'max-h-0'
}`}
>
<div className="px-6 py-4 text-zinc-300 border-t border-zinc-700">
<p>{faq.answer}</p>
</div>
</div>
</div>
))}
</div>
<div className="mt-8 text-center">
<p className="text-zinc-400">
{t('faq.moreQuestions', 'Have more questions?')}{' '}
<a
href="/contact"
className="text-white hover:text-zinc-300 underline transition-colors"
>
{t('faq.contactUs', 'Contact us')}
</a>
</p>
</div>
</section>
);
}
+67
View File
@@ -0,0 +1,67 @@
import React from 'react';
import { useRouter } from 'react-router-dom';
import { Helmet } from 'react-helmet-async';
import { useTranslation } from 'react-i18next';
export function HreflangTags() {
const { i18n } = useTranslation();
const location = window.location;
const currentPath = location.pathname;
const siteUrl = 'https://damjan-savic.com';
const supportedLanguages = ['de', 'en', 'sr'];
const defaultLanguage = 'de';
// Extract current language from URL
const pathSegments = currentPath.split('/').filter(Boolean);
const currentLang = supportedLanguages.includes(pathSegments[0]) ? pathSegments[0] : defaultLanguage;
// Get path without language prefix
const pathWithoutLang = currentLang === defaultLanguage
? currentPath
: currentPath.replace(`/${currentLang}`, '') || '/';
// Generate hreflang URLs
const generateHreflangUrl = (lang: string) => {
if (lang === defaultLanguage) {
return `${siteUrl}${pathWithoutLang}`;
}
return `${siteUrl}/${lang}${pathWithoutLang}`;
};
return (
<Helmet>
{/* Hreflang tags for all supported languages */}
{supportedLanguages.map((lang) => (
<link
key={lang}
rel="alternate"
hrefLang={lang}
href={generateHreflangUrl(lang)}
/>
))}
{/* x-default hreflang */}
<link
rel="alternate"
hrefLang="x-default"
href={generateHreflangUrl(defaultLanguage)}
/>
{/* Additional language meta tags */}
<html lang={currentLang} />
<meta property="og:locale" content={currentLang === 'de' ? 'de_DE' : currentLang === 'en' ? 'en_US' : 'sr_RS'} />
{/* Alternate og:locale tags */}
{supportedLanguages
.filter(lang => lang !== currentLang)
.map(lang => (
<meta
key={`og-locale-${lang}`}
property="og:locale:alternate"
content={lang === 'de' ? 'de_DE' : lang === 'en' ? 'en_US' : 'sr_RS'}
/>
))}
</Helmet>
);
}
+4
View File
@@ -19,6 +19,7 @@ import LanguageSwitcher from "./LanguageSwitcher";
import { Link, useLocation } from "react-router-dom";
import Footer from "./Footer";
import { useScrollContext } from "./ScrollContext";
import PerformanceMonitor from "./PerformanceMonitor";
interface LayoutProps {
children: ReactNode;
@@ -252,6 +253,9 @@ const Layout = ({ children }: LayoutProps) => {
{/* Footer */}
<Footer />
{/* Performance Monitor */}
<PerformanceMonitor />
</div>
);
};
+199
View File
@@ -0,0 +1,199 @@
import React, { useState, useEffect } from 'react';
import { Activity, TrendingUp, AlertCircle, CheckCircle } from 'lucide-react';
interface WebVitalMetric {
name: string;
value: number;
rating: 'good' | 'needs-improvement' | 'poor';
timestamp: number;
}
interface PerformanceMetrics {
lcp: WebVitalMetric[];
inp: WebVitalMetric[];
cls: WebVitalMetric[];
fcp: WebVitalMetric[];
ttfb: WebVitalMetric[];
}
const THRESHOLDS = {
LCP: { good: 2500, poor: 4000 },
INP: { good: 200, poor: 500 },
CLS: { good: 0.1, poor: 0.25 },
FCP: { good: 1800, poor: 3000 },
TTFB: { good: 800, poor: 1800 }
};
export function PerformanceMonitor() {
const [metrics, setMetrics] = useState<PerformanceMetrics>({
lcp: [],
inp: [],
cls: [],
fcp: [],
ttfb: []
});
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
// Listen for performance metrics from webVitals
const handlePerformanceData = (event: CustomEvent) => {
const metric = event.detail as WebVitalMetric;
setMetrics(prev => ({
...prev,
[metric.name.toLowerCase()]: [...(prev[metric.name.toLowerCase() as keyof PerformanceMetrics] || []), metric].slice(-10)
}));
};
window.addEventListener('web-vitals', handlePerformanceData as EventListener);
return () => {
window.removeEventListener('web-vitals', handlePerformanceData as EventListener);
};
}, []);
const getLatestMetric = (metricName: keyof PerformanceMetrics) => {
const metricArray = metrics[metricName];
return metricArray[metricArray.length - 1];
};
const getRatingColor = (rating?: string) => {
switch (rating) {
case 'good': return 'text-green-500';
case 'needs-improvement': return 'text-yellow-500';
case 'poor': return 'text-red-500';
default: return 'text-zinc-400';
}
};
const getRatingIcon = (rating?: string) => {
switch (rating) {
case 'good': return <CheckCircle className="h-4 w-4" />;
case 'needs-improvement': return <AlertCircle className="h-4 w-4" />;
case 'poor': return <AlertCircle className="h-4 w-4" />;
default: return null;
}
};
const formatValue = (name: string, value?: number) => {
if (value === undefined) return '-';
if (name === 'CLS') return value.toFixed(3);
return `${Math.round(value)}ms`;
};
const calculateScore = () => {
const latestLCP = getLatestMetric('lcp');
const latestINP = getLatestMetric('inp');
const latestCLS = getLatestMetric('cls');
if (!latestLCP || !latestINP || !latestCLS) return null;
let score = 100;
// LCP scoring
if (latestLCP.value > THRESHOLDS.LCP.poor) score -= 33;
else if (latestLCP.value > THRESHOLDS.LCP.good) score -= 16;
// INP scoring
if (latestINP.value > THRESHOLDS.INP.poor) score -= 33;
else if (latestINP.value > THRESHOLDS.INP.good) score -= 16;
// CLS scoring
if (latestCLS.value > THRESHOLDS.CLS.poor) score -= 34;
else if (latestCLS.value > THRESHOLDS.CLS.good) score -= 18;
return Math.max(0, Math.round(score));
};
// Only show in development or with special flag
if (process.env.NODE_ENV === 'production' && !window.location.search.includes('debug=true')) {
return null;
}
return (
<>
{/* Toggle Button */}
<button
onClick={() => setIsVisible(!isVisible)}
className="fixed bottom-4 right-4 z-50 p-3 bg-zinc-800 border border-zinc-700 rounded-full shadow-lg hover:bg-zinc-700 transition-all"
aria-label="Toggle Performance Monitor"
>
<Activity className="h-5 w-5 text-white" />
</button>
{/* Performance Panel */}
{isVisible && (
<div className="fixed bottom-20 right-4 z-50 w-80 bg-zinc-900 border border-zinc-800 rounded-lg shadow-xl p-4">
<div className="flex items-center justify-between mb-4">
<h3 className="text-lg font-semibold text-white flex items-center gap-2">
<Activity className="h-5 w-5" />
Core Web Vitals
</h3>
<button
onClick={() => setIsVisible(false)}
className="text-zinc-400 hover:text-white"
>
×
</button>
</div>
{/* Overall Score */}
{calculateScore() !== null && (
<div className="mb-4 p-3 bg-zinc-800 rounded-lg">
<div className="flex items-center justify-between">
<span className="text-sm text-zinc-400">Performance Score</span>
<span className={`text-2xl font-bold ${
calculateScore()! >= 90 ? 'text-green-500' :
calculateScore()! >= 50 ? 'text-yellow-500' :
'text-red-500'
}`}>
{calculateScore()}
</span>
</div>
</div>
)}
{/* Metrics */}
<div className="space-y-3">
{[
{ key: 'lcp', label: 'Largest Contentful Paint', threshold: THRESHOLDS.LCP },
{ key: 'inp', label: 'Interaction to Next Paint', threshold: THRESHOLDS.INP },
{ key: 'cls', label: 'Cumulative Layout Shift', threshold: THRESHOLDS.CLS },
{ key: 'fcp', label: 'First Contentful Paint', threshold: THRESHOLDS.FCP },
{ key: 'ttfb', label: 'Time to First Byte', threshold: THRESHOLDS.TTFB }
].map(({ key, label }) => {
const latest = getLatestMetric(key as keyof PerformanceMetrics);
return (
<div key={key} className="flex items-center justify-between p-2 bg-zinc-800/50 rounded">
<div className="flex-1">
<div className="text-sm text-zinc-300">{label}</div>
<div className="text-xs text-zinc-500 uppercase">{key}</div>
</div>
<div className={`flex items-center gap-2 ${getRatingColor(latest?.rating)}`}>
{getRatingIcon(latest?.rating)}
<span className="font-mono text-sm">
{formatValue(key.toUpperCase(), latest?.value)}
</span>
</div>
</div>
);
})}
</div>
{/* Info */}
<div className="mt-4 pt-4 border-t border-zinc-800">
<p className="text-xs text-zinc-500">
Real user metrics Updates live
<a
href="https://web.dev/vitals/"
target="_blank"
rel="noopener noreferrer"
className="ml-1 text-blue-400 hover:text-blue-300"
>
Learn more
</a>
</p>
</div>
</div>
)}
</>
);
}
+161
View File
@@ -0,0 +1,161 @@
import React from 'react';
import { ExternalLink, Github, Globe, Zap, TrendingUp, Users } from 'lucide-react';
import { ProjectSchema } from './schemas/ProjectSchema';
interface ProjectShowcaseProps {
project: {
id: string;
title: string;
description: string;
technicalChallenge: string;
businessImpact: {
roi?: string;
performanceGain?: string;
usersImpacted?: string;
};
technologies: string[];
category: string;
dateCreated: string;
liveUrl?: string;
githubUrl?: string;
screenshotUrl?: string;
features: string[];
codeExample?: string;
};
}
export function ProjectShowcase({ project }: ProjectShowcaseProps) {
return (
<article className="project-showcase bg-zinc-900 rounded-lg overflow-hidden">
<ProjectSchema project={project} />
{/* Hero Section with Screenshot */}
{project.screenshotUrl && (
<div className="relative aspect-video">
<img
src={project.screenshotUrl}
alt={`Screenshot of ${project.title}`}
className="w-full h-full object-cover"
loading="lazy"
/>
<div className="absolute inset-0 bg-gradient-to-t from-zinc-900 via-transparent to-transparent" />
</div>
)}
<div className="p-8 space-y-8">
{/* Title and Description */}
<header>
<h2 className="text-3xl font-bold text-white mb-4">{project.title}</h2>
<p className="text-lg text-zinc-300 leading-relaxed">{project.description}</p>
</header>
{/* For Recruiters: Technical Challenge */}
<section className="technical-challenge">
<div className="flex items-center gap-2 mb-3">
<Zap className="h-5 w-5 text-blue-500" />
<h3 className="text-xl font-semibold text-white">Technical Challenge</h3>
</div>
<p className="text-zinc-300 bg-zinc-800/50 p-4 rounded-lg border border-zinc-700">
{project.technicalChallenge}
</p>
</section>
{/* For Clients: Business Impact */}
<section className="business-impact">
<div className="flex items-center gap-2 mb-3">
<TrendingUp className="h-5 w-5 text-green-500" />
<h3 className="text-xl font-semibold text-white">Business Impact</h3>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{project.businessImpact.roi && (
<div className="bg-zinc-800/50 p-4 rounded-lg border border-zinc-700">
<p className="text-sm text-zinc-400 mb-1">ROI</p>
<p className="text-2xl font-bold text-green-400">{project.businessImpact.roi}</p>
</div>
)}
{project.businessImpact.performanceGain && (
<div className="bg-zinc-800/50 p-4 rounded-lg border border-zinc-700">
<p className="text-sm text-zinc-400 mb-1">Performance</p>
<p className="text-2xl font-bold text-blue-400">{project.businessImpact.performanceGain}</p>
</div>
)}
{project.businessImpact.usersImpacted && (
<div className="bg-zinc-800/50 p-4 rounded-lg border border-zinc-700">
<p className="text-sm text-zinc-400 mb-1">Users Impacted</p>
<p className="text-2xl font-bold text-purple-400">{project.businessImpact.usersImpacted}</p>
</div>
)}
</div>
</section>
{/* Technology Stack with Keywords */}
<section className="tech-stack">
<h3 className="text-xl font-semibold text-white mb-3">Technologies Used</h3>
<div className="flex flex-wrap gap-2">
{project.technologies.map(tech => (
<span
key={tech}
className="tech-tag px-3 py-1 bg-zinc-800 border border-zinc-700 text-zinc-300 rounded-full text-sm hover:border-zinc-600 transition-colors"
>
{tech}
</span>
))}
</div>
</section>
{/* Key Features */}
{project.features && project.features.length > 0 && (
<section className="features">
<h3 className="text-xl font-semibold text-white mb-3">Key Features</h3>
<ul className="space-y-2">
{project.features.map((feature, index) => (
<li key={index} className="flex items-start gap-2">
<span className="text-blue-500 mt-0.5"></span>
<span className="text-zinc-300">{feature}</span>
</li>
))}
</ul>
</section>
)}
{/* Code Quality Showcase */}
{project.codeExample && (
<section className="code-showcase">
<h3 className="text-xl font-semibold text-white mb-3">Code Example</h3>
<pre className="bg-zinc-950 p-4 rounded-lg overflow-x-auto border border-zinc-800">
<code className="text-sm text-zinc-300 font-mono">
{project.codeExample}
</code>
</pre>
</section>
)}
{/* Links */}
<div className="project-links flex gap-4 pt-4">
{project.liveUrl && (
<a
href={project.liveUrl}
rel="noopener noreferrer"
target="_blank"
className="flex items-center gap-2 px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors"
>
<Globe className="h-4 w-4" />
View Live Project
</a>
)}
{project.githubUrl && (
<a
href={project.githubUrl}
rel="noopener noreferrer"
target="_blank"
className="flex items-center gap-2 px-4 py-2 bg-zinc-800 hover:bg-zinc-700 text-white border border-zinc-700 rounded-lg transition-colors"
>
<Github className="h-4 w-4" />
View on GitHub
</a>
)}
</div>
</div>
</article>
);
}
+79 -30
View File
@@ -1,6 +1,10 @@
import React from 'react';
import { Helmet } from 'react-helmet-async';
import { useTranslation } from 'react-i18next';
import { HreflangTags } from './HreflangTags';
import { seoContent as seoContentDe } from '../i18n/locales/de/seo-content';
import { seoContent as seoContentEn } from '../i18n/locales/en/seo-content';
import { AutoBreadcrumbs } from './schemas/BreadcrumbSchema';
interface SEOProps {
title?: string;
@@ -8,14 +12,22 @@ interface SEOProps {
image?: string;
article?: boolean;
schema?: object;
keywords?: string;
author?: string;
datePublished?: string;
dateModified?: string;
}
const SEO: React.FC<SEOProps> = ({
title,
description = 'JTL Integration Expert and Digital Commerce Specialist with extensive experience in e-commerce solutions and warehouse management systems.',
image = 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80',
description,
image = '/portrait.jpg',
article = false,
schema,
keywords,
author = 'Damjan Savić',
datePublished,
dateModified,
}) => {
// Nur i18n wird benötigt t wurde entfernt, da es nicht genutzt wird.
const { i18n } = useTranslation();
@@ -24,31 +36,72 @@ const SEO: React.FC<SEOProps> = ({
const siteUrl = 'https://damjan-savic.com';
const currentUrl = typeof window !== 'undefined' ? window.location.href : siteUrl;
const currentLanguage = i18n.language;
// SEO-Content basierend auf Sprache auswählen
const seoContent = currentLanguage === 'de' ? seoContentDe : seoContentEn;
const defaultDescription = description || seoContent.hero.description;
// Default schema für die Website
const defaultSchema = {
'@context': 'https://schema.org',
'@type': 'Person',
name: 'Damjan Savić',
alternateName: 'Damjan Savic',
url: siteUrl,
image: image,
description: description,
image: `${siteUrl}${image}`,
description: defaultDescription,
sameAs: [
'https://linkedin.com/in/damjansavic',
'https://github.com/damjansavic'
],
jobTitle: 'JTL Integration Expert',
jobTitle: ['Fullstack Developer', 'Software Engineer', 'KI Spezialist'],
worksFor: {
'@type': 'Organization',
name: 'Independent Consultant'
name: 'CoderConda'
},
knowsAbout: [
'JTL-Wawi',
'E-commerce',
'Warehouse Management Systems',
'Digital Commerce',
'System Integration'
]
'Python Development',
'JavaScript Development',
'React.js',
'Next.js',
'TypeScript',
'Electron Desktop Applications',
'Künstliche Intelligenz (KI/AI)',
'OLLAMA AI/ML',
'ERP Systems Integration',
'E-Commerce Development',
'Process Automation',
'Backend Development',
'Frontend Development',
'Full Stack Development'
],
hasSkill: [
{
'@type': 'DefinedTerm',
name: 'Python Development',
description: 'Expert-level Python programming for automation, backend development, and AI/ML applications'
},
{
'@type': 'DefinedTerm',
name: 'JavaScript/TypeScript Development',
description: 'Full-stack JavaScript development with React, Next.js, Node.js, and TypeScript'
},
{
'@type': 'DefinedTerm',
name: 'AI/ML with OLLAMA',
description: 'Implementation of AI solutions using OLLAMA for local language models'
},
{
'@type': 'DefinedTerm',
name: 'ERP & E-Commerce Integration',
description: 'Custom ERP system development and e-commerce platform integration'
}
],
address: {
'@type': 'PostalAddress',
addressCountry: 'DE',
addressLocality: 'Köln'
}
};
// Sicherstellen, dass supportedLngs ein Array ist, bevor .filter verwendet wird
@@ -62,26 +115,21 @@ const SEO: React.FC<SEOProps> = ({
: [];
return (
<Helmet>
{/* Basic meta tags */}
<html lang={currentLanguage} />
<title>{fullTitle}</title>
<meta name="description" content={description} />
<meta name="image" content={image} />
<link rel="canonical" href={currentUrl} />
{/* Language alternates */}
{alternateUrls?.map(
({ hrefLang, href }: { hrefLang: string; href: string }) => (
<link key={hrefLang} rel="alternate" hrefLang={hrefLang} href={href} />
)
)}
<link rel="alternate" hrefLang="x-default" href={siteUrl} />
<>
<HreflangTags />
<AutoBreadcrumbs />
<Helmet>
{/* Basic meta tags */}
<html lang={currentLanguage} />
<title>{fullTitle}</title>
<meta name="description" content={defaultDescription} />
<meta name="image" content={image} />
<link rel="canonical" href={currentUrl} />
{/* Open Graph meta tags */}
<meta property="og:url" content={currentUrl} />
<meta property="og:title" content={fullTitle} />
<meta property="og:description" content={description} />
<meta property="og:description" content={defaultDescription} />
<meta property="og:image" content={image} />
<meta property="og:type" content={article ? 'article' : 'website'} />
<meta property="og:site_name" content={siteTitle} />
@@ -95,7 +143,7 @@ const SEO: React.FC<SEOProps> = ({
{/* Twitter Card meta tags */}
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={fullTitle} />
<meta name="twitter:description" content={description} />
<meta name="twitter:description" content={defaultDescription} />
<meta name="twitter:image" content={image} />
{/* Additional meta tags */}
@@ -103,7 +151,7 @@ const SEO: React.FC<SEOProps> = ({
<meta name="theme-color" content="#000000" />
<meta
name="keywords"
content="JTL, E-commerce, Warehouse Management, Digital Commerce, System Integration, JTL-Wawi, WMS"
content={keywords || "Damjan Savić, Fullstack Developer, Python, JavaScript, React, Next.js, TypeScript, Electron, KI, AI, OLLAMA, ERP, E-Commerce, Prozessautomatisierung, Backend, Frontend, Web Development"}
/>
<meta name="author" content="Damjan Savić" />
@@ -112,6 +160,7 @@ const SEO: React.FC<SEOProps> = ({
{JSON.stringify(schema || defaultSchema)}
</script>
</Helmet>
</>
);
};
+102
View File
@@ -0,0 +1,102 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
interface BreadcrumbItem {
name: string;
url: string;
}
interface BreadcrumbSchemaProps {
items: BreadcrumbItem[];
}
export function BreadcrumbSchema({ items }: BreadcrumbSchemaProps) {
const schema = {
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": items.map((item, index) => ({
"@type": "ListItem",
"position": index + 1,
"name": item.name,
"item": item.url
}))
};
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
);
}
// Hook to generate breadcrumb items based on current route
export function useBreadcrumbs(pathname: string) {
const { t, i18n } = useTranslation();
const siteUrl = 'https://damjan-savic.com';
const currentLang = i18n.language;
const segments = pathname.split('/').filter(Boolean);
const breadcrumbs: BreadcrumbItem[] = [];
// Always add home
breadcrumbs.push({
name: t('navigation.home', 'Home'),
url: currentLang === 'de' ? siteUrl : `${siteUrl}/${currentLang}`
});
// Build breadcrumbs from path segments
let currentPath = currentLang === 'de' ? '' : `/${currentLang}`;
segments.forEach((segment, index) => {
// Skip language segment
if (index === 0 && ['en', 'de', 'sr'].includes(segment)) {
return;
}
currentPath += `/${segment}`;
// Map segment to readable name
let name = segment;
switch (segment) {
case 'portfolio':
name = t('navigation.portfolio', 'Portfolio');
break;
case 'blog':
name = t('navigation.blog', 'Blog');
break;
case 'about':
name = t('navigation.about', 'About');
break;
case 'contact':
name = t('navigation.contact', 'Contact');
break;
default:
// For dynamic segments like project names, format them nicely
name = segment
.split('-')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
}
breadcrumbs.push({
name,
url: `${siteUrl}${currentPath}`
});
});
return breadcrumbs;
}
// Component with automatic breadcrumb generation
export function AutoBreadcrumbs() {
const pathname = window.location.pathname;
const breadcrumbs = useBreadcrumbs(pathname);
// Don't show breadcrumbs on home page
if (breadcrumbs.length <= 1) {
return null;
}
return <BreadcrumbSchema items={breadcrumbs} />;
}
+160
View File
@@ -0,0 +1,160 @@
import React from 'react';
interface HowToStep {
name: string;
text: string;
image?: string;
url?: string;
}
interface HowToSchemaProps {
title: string;
description: string;
totalTime?: string;
estimatedCost?: {
value: string;
currency: string;
};
supply?: string[];
tool?: string[];
steps: HowToStep[];
image?: string;
video?: {
name: string;
description: string;
thumbnailUrl: string;
uploadDate: string;
duration: string;
embedUrl: string;
};
}
export function HowToSchema({
title,
description,
totalTime,
estimatedCost,
supply,
tool,
steps,
image,
video
}: HowToSchemaProps) {
const schema = {
"@context": "https://schema.org",
"@type": "HowTo",
"name": title,
"description": description,
"image": image,
...(totalTime && { "totalTime": totalTime }),
...(estimatedCost && {
"estimatedCost": {
"@type": "MonetaryAmount",
"currency": estimatedCost.currency,
"value": estimatedCost.value
}
}),
...(supply && supply.length > 0 && {
"supply": supply.map(item => ({
"@type": "HowToSupply",
"name": item
}))
}),
...(tool && tool.length > 0 && {
"tool": tool.map(item => ({
"@type": "HowToTool",
"name": item
}))
}),
"step": steps.map((step, index) => ({
"@type": "HowToStep",
"name": step.name,
"text": step.text,
"position": index + 1,
...(step.image && { "image": step.image }),
...(step.url && { "url": step.url })
})),
...(video && {
"video": {
"@type": "VideoObject",
"name": video.name,
"description": video.description,
"thumbnailUrl": video.thumbnailUrl,
"uploadDate": video.uploadDate,
"duration": video.duration,
"embedUrl": video.embedUrl
}
})
};
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
);
}
// Example HowTo content for common queries
export const howToExamples = {
pythonAutomation: {
title: "Wie kann ich Python für Prozessautomatisierung einsetzen?",
description: "Schritt-für-Schritt Anleitung zur Automatisierung von Geschäftsprozessen mit Python",
totalTime: "PT30M",
tool: ["Python 3.8+", "pip", "Virtual Environment", "Code Editor (VS Code)"],
steps: [
{
name: "Python-Umgebung einrichten",
text: "Installieren Sie Python 3.8 oder höher und erstellen Sie eine virtuelle Umgebung mit 'python -m venv automation-env'"
},
{
name: "Notwendige Libraries installieren",
text: "Aktivieren Sie die virtuelle Umgebung und installieren Sie benötigte Pakete: pip install selenium pandas schedule requests"
},
{
name: "Automatisierungsskript erstellen",
text: "Erstellen Sie ein Python-Skript, das Ihre spezifischen Aufgaben automatisiert. Nutzen Sie Selenium für Web-Automation, Pandas für Datenverarbeitung"
},
{
name: "Zeitgesteuerte Ausführung einrichten",
text: "Verwenden Sie die Schedule-Library oder Cron-Jobs (Linux/Mac) bzw. Task Scheduler (Windows) für regelmäßige Ausführung"
},
{
name: "Monitoring und Logging implementieren",
text: "Fügen Sie Logging hinzu, um Fehler zu tracken und den Erfolg der Automatisierung zu überwachen"
}
]
},
ollamaSetup: {
title: "How to Set Up OLLAMA for Local AI Development",
description: "Complete guide to installing and using OLLAMA for privacy-focused AI applications",
totalTime: "PT45M",
tool: ["OLLAMA CLI", "Python 3.8+", "8GB+ RAM", "Terminal/Command Prompt"],
steps: [
{
name: "Install OLLAMA",
text: "Download and install OLLAMA from ollama.ai. For Mac/Linux: curl -fsSL https://ollama.ai/install.sh | sh"
},
{
name: "Download a Language Model",
text: "Pull a model like Llama 2: ollama pull llama2. This downloads the model locally to your machine"
},
{
name: "Test the Model",
text: "Run the model interactively: ollama run llama2. Ask a test question to verify it's working"
},
{
name: "Install Python Integration",
text: "Install the Python library: pip install ollama. This allows you to use OLLAMA in your Python applications"
},
{
name: "Create Your First Application",
text: "Write Python code to interact with OLLAMA: import ollama; response = ollama.chat(model='llama2', messages=[{'role': 'user', 'content': 'Hello'}])"
},
{
name: "Optimize for Production",
text: "Configure model parameters, implement caching, and set up proper error handling for production use"
}
]
}
};
+85
View File
@@ -0,0 +1,85 @@
import React from 'react';
export function PersonSchema() {
const personSchema = {
"@context": "https://schema.org",
"@type": "Person",
"@id": "https://damjan-savic.com/#person",
"name": "Damjan Savić",
"alternateName": "Damjan Savic",
"jobTitle": ["Fullstack Developer", "Software Engineer", "KI Spezialist"],
"description": "Erfahrener Fullstack Entwickler spezialisiert auf Python, JavaScript, React, Next.js, TypeScript, Electron Desktop Apps, Automatisierungslösungen, KI/ML mit OLLAMA, ERP-Systeme, E-Commerce und Prozessautomatisierung",
"url": "https://damjan-savic.com",
"image": "https://damjan-savic.com/portrait.jpg",
"sameAs": [
"https://github.com/damjansavic",
"https://linkedin.com/in/damjansavic"
],
"knowsAbout": [
"Python Development",
"JavaScript Development",
"React.js",
"Next.js",
"TypeScript",
"Electron Desktop Applications",
"Künstliche Intelligenz (KI/AI)",
"OLLAMA AI/ML",
"ERP Systems Integration",
"E-Commerce Development",
"Process Automation",
"Backend Development",
"Frontend Development",
"Full Stack Development"
],
"hasSkill": [
{
"@type": "DefinedTerm",
"name": "Python Development",
"description": "Expert-level Python programming for automation, backend development, and AI/ML applications"
},
{
"@type": "DefinedTerm",
"name": "JavaScript/TypeScript Development",
"description": "Full-stack JavaScript development with React, Next.js, Node.js, and TypeScript"
},
{
"@type": "DefinedTerm",
"name": "AI/ML with OLLAMA",
"description": "Implementation of AI solutions using OLLAMA for local language models"
},
{
"@type": "DefinedTerm",
"name": "ERP & E-Commerce Integration",
"description": "Custom ERP system development and e-commerce platform integration"
},
{
"@type": "DefinedTerm",
"name": "Electron Desktop Development",
"description": "Cross-platform desktop application development with Electron and modern web technologies"
},
{
"@type": "DefinedTerm",
"name": "Process Automation",
"description": "Business process automation using Python, APIs, and custom integration solutions"
}
],
"address": {
"@type": "PostalAddress",
"addressLocality": "Köln",
"addressRegion": "NRW",
"addressCountry": "DE"
},
"worksFor": {
"@type": "Organization",
"name": "CoderConda",
"description": "Moderne Softwareentwicklung, KI-Integration und Prozessautomatisierung"
}
};
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(personSchema) }}
/>
);
}
+58
View File
@@ -0,0 +1,58 @@
import React from 'react';
interface Project {
title: string;
description: string;
category: string;
technologies: string[];
dateCreated: string;
url?: string;
liveUrl?: string;
githubUrl?: string;
screenshotUrl?: string;
features?: string[];
technicalChallenge?: string;
businessImpact?: {
roi?: string;
performanceGain?: string;
usersImpacted?: string;
};
}
interface ProjectSchemaProps {
project: Project;
}
export function ProjectSchema({ project }: ProjectSchemaProps) {
const schema = {
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": project.title,
"description": project.description,
"applicationCategory": project.category,
"operatingSystem": "Web-based",
"programmingLanguage": project.technologies,
"author": {
"@type": "Person",
"name": "Damjan Savić",
"@id": "https://damjan-savic.com/#person"
},
"dateCreated": project.dateCreated,
"url": project.url || project.liveUrl,
"screenshot": project.screenshotUrl,
"featureList": project.features || [],
"softwareRequirements": "Modern web browser with JavaScript enabled",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "EUR"
}
};
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
);
}
+42 -23
View File
@@ -1,24 +1,33 @@
export const meta = {
site: {
name: 'Damjan Savić',
title: 'Damjan Savić - Digital Solutions & JTL Integration',
subtitle: 'Entwickler individueller Lösungen zur Automatisierung von Prozessen',
description: 'Mit einem Hintergrund in der Softwareentwicklung und Expertise in der digitalen Transformation unterstütze ich Unternehmen bei der Optimierung und Automatisierung ihrer Geschäftsprozesse.',
title: 'Damjan Savić - Fullstack Entwickler | Python, JavaScript, KI',
subtitle: 'Python, JavaScript & KI Spezialist für moderne Webanwendungen und Automatisierung',
description: 'Erfahrener Fullstack Entwickler spezialisiert auf Python, JavaScript, React, Next.js, TypeScript, Electron Desktop Apps, KI/AI mit OLLAMA, ERP-Systeme, E-Commerce und Prozessautomatisierung.',
keywords: [
'JTL Integration',
'E-Commerce Lösungen',
'Prozessautomatisierung',
'Digitale Beratung',
'ERP Systeme',
'Damjan Savić',
'Fullstack Entwickler',
'Python Entwicklung',
'Systemintegration',
'Digitales Marketing'
'JavaScript Entwicklung',
'React.js',
'Next.js',
'TypeScript',
'Electron Desktop Apps',
'Künstliche Intelligenz KI AI',
'OLLAMA AI ML',
'ERP Integration',
'E-Commerce Entwicklung',
'Prozessautomatisierung',
'Backend Entwicklung',
'Frontend Entwicklung',
'Web Development',
'Software Engineering'
].join(', ')
},
company: {
name: 'CoderConda',
shortName: 'CoderConda',
description: 'Ihr JTL-Servicepartner für schnelle, verbindliche und kosteneffiziente Lösungen.',
description: 'Ihr Partner für moderne Softwareentwicklung, KI-Integration und Prozessautomatisierung.',
address: 'Rotdornallee',
city: 'Köln',
postalCode: '50999',
@@ -29,7 +38,7 @@ export const meta = {
},
author: {
name: 'Damjan Savić',
role: 'Consultant Digital Solutions',
role: 'Fullstack Developer & KI Spezialist',
company: 'CoderConda',
location: 'Köln, Deutschland',
email: 'info@damjan-savic.com',
@@ -50,21 +59,31 @@ export const meta = {
},
expertise: {
areas: [
'JTL Integration & Beratung',
'Python Backend Entwicklung',
'JavaScript/TypeScript Frontend',
'KI/ML Integration mit OLLAMA',
'Prozessautomatisierung',
'E-Commerce Entwicklung',
'Systemintegration',
'ERP-Systeme',
'Digitale Transformation'
'ERP System Integration',
'Electron Desktop Apps',
'React & Next.js Anwendungen'
],
technologies: [
'Python',
'React/Next.js',
'MariaDB',
'JavaScript',
'TypeScript',
'React',
'Next.js',
'Node.js',
'OLLAMA',
'Electron',
'FastAPI',
'Django',
'PostgreSQL',
'MongoDB',
'Docker',
'JTL-WaWi',
'Shopify',
'Shopware'
'Kubernetes',
'AWS'
]
},
seo: {
@@ -75,12 +94,12 @@ export const meta = {
og: {
type: 'website',
locale: 'de_DE',
siteName: 'Damjan Savić - Digital Solutions',
siteName: 'Damjan Savić - Fullstack Developer Portfolio',
images: {
url: '/images/og-image.jpg',
width: 1200,
height: 630,
alt: 'Damjan Savić - Digital Solutions & JTL Integration'
alt: 'Damjan Savić - Fullstack Developer | Python, JavaScript, KI/OLLAMA'
}
},
twitter: {
+94
View File
@@ -0,0 +1,94 @@
export const seoContent = {
hero: {
title: "Damjan Savić - Fullstack Entwickler",
subtitle: "Python, JavaScript & KI Spezialist",
description: "Entwicklung moderner Webanwendungen, Automatisierungslösungen und KI-Integration mit OLLAMA",
cta: {
primary: "Projekte ansehen",
secondary: "Kontakt aufnehmen"
}
},
services: {
title: "Meine Expertise",
subtitle: "Spezialisiert auf moderne Technologien und Lösungen",
items: {
backend: {
title: "Backend Entwicklung mit Python & Node.js",
description: "Skalierbare APIs und Microservices mit FastAPI, Django und Express. Fokus auf Performance und Clean Architecture.",
keywords: ["Python Backend", "FastAPI", "Django", "Node.js", "REST API", "GraphQL"]
},
frontend: {
title: "Frontend Development mit React & Next.js",
description: "Moderne, responsive Webanwendungen mit React, Next.js und TypeScript. SEO-optimiert und performant.",
keywords: ["React", "Next.js", "TypeScript", "Tailwind CSS", "Redux", "SPA"]
},
ai: {
title: "KI/ML Integration mit OLLAMA",
description: "Lokale KI-Lösungen mit OLLAMA für Datenschutz und Kosteneffizienz. Custom LLM-Integration für Ihre Anwendungen.",
keywords: ["OLLAMA", "KI Integration", "Machine Learning", "LLM", "AI Development", "NLP"]
},
erp: {
title: "ERP System Integration & Entwicklung",
description: "Nahtlose Integration von ERP-Systemen. Custom-Module und Automatisierung von Geschäftsprozessen.",
keywords: ["ERP Integration", "SAP", "Odoo", "Business Automation", "System Integration"]
},
ecommerce: {
title: "E-Commerce & Onlineshop Lösungen",
description: "Entwicklung und Optimierung von E-Commerce-Plattformen. Performance-Optimierung und Conversion-Steigerung.",
keywords: ["E-Commerce", "Shopify", "WooCommerce", "Payment Integration", "Shop-Systeme"]
},
automation: {
title: "Prozessautomatisierung & Workflow Optimierung",
description: "Automatisierung repetitiver Aufgaben mit Python. RPA und Custom-Automation-Tools für Effizienzsteigerung.",
keywords: ["Prozessautomatisierung", "Python Automation", "RPA", "Workflow", "Selenium", "Task Automation"]
}
}
},
skills: {
title: "Technologie-Stack",
categories: {
languages: {
title: "Programmiersprachen",
items: ["Python", "JavaScript", "TypeScript", "SQL", "Bash"]
},
backend: {
title: "Backend Technologies",
items: ["FastAPI", "Django", "Node.js", "Express", "PostgreSQL", "MongoDB", "Redis"]
},
frontend: {
title: "Frontend Technologies",
items: ["React", "Next.js", "Vue.js", "Tailwind CSS", "Material-UI", "Framer Motion"]
},
ai: {
title: "AI/ML Tools",
items: ["OLLAMA", "LangChain", "OpenAI API", "Hugging Face", "TensorFlow", "PyTorch"]
},
devops: {
title: "DevOps & Tools",
items: ["Docker", "Kubernetes", "AWS", "CI/CD", "Git", "Linux"]
}
}
},
cta: {
title: "Bereit für Ihr nächstes Projekt?",
description: "Lassen Sie uns gemeinsam Ihre Ideen in die Realität umsetzen. Von der Konzeption bis zur Implementierung.",
button: "Kostenloses Erstgespräch vereinbaren"
},
testimonials: {
title: "Was Kunden sagen",
items: [
{
text: "Damjan hat unsere Python-Skripte in eine moderne Web-App transformiert. Die Automatisierung spart uns täglich 4 Stunden Arbeit.",
author: "Michael Schmidt",
role: "CTO, TechStart GmbH",
keywords: ["Python Automatisierung", "Web App Entwicklung"]
},
{
text: "Die OLLAMA-Integration ermöglicht uns KI-Features ohne Cloud-Kosten. Datenschutz und Performance sind erstklassig.",
author: "Sarah Weber",
role: "Product Owner, DataSec AG",
keywords: ["OLLAMA Integration", "Datenschutz KI"]
}
]
}
}
+44 -25
View File
@@ -1,24 +1,33 @@
export const meta = {
site: {
name: 'Damjan Savić',
title: 'Damjan Savić - Digital Solutions & JTL Integration',
subtitle: 'Developer of custom solutions for process automation',
description: 'With a background in software development and expertise in digital transformation, I help companies optimize and automate their business processes.',
title: 'Damjan Savić - Fullstack Developer | Python, JavaScript, AI',
subtitle: 'Python, JavaScript & AI Specialist for Modern Web Applications',
description: 'Experienced Fullstack Developer specializing in Python, JavaScript, React, Next.js, TypeScript, Electron Desktop Apps, AI/ML with OLLAMA, ERP systems, e-commerce, and process automation.',
keywords: [
'JTL Integration',
'E-Commerce Solutions',
'Process Automation',
'Digital Consulting',
'ERP Systems',
'Damjan Savić',
'Fullstack Developer',
'Python Development',
'System Integration',
'Digital Marketing'
'JavaScript Development',
'React.js',
'Next.js',
'TypeScript',
'Electron Desktop Applications',
'Artificial Intelligence AI',
'OLLAMA AI ML',
'ERP Integration',
'E-Commerce Development',
'Process Automation',
'Backend Development',
'Frontend Development',
'Web Development',
'Software Engineering'
].join(', ')
},
company: {
name: 'Damjan Savić',
shortName: 'Damjan Savić',
description: 'Your software development partner for fast, reliable, and cost-efficient solutions.',
description: 'Your partner for modern software development, AI integration, and process automation.',
address: 'Rotdornallee',
city: 'Köln',
postalCode: '50999',
@@ -29,9 +38,9 @@ export const meta = {
},
author: {
name: 'Damjan Savić',
role: 'Consultant Digital Solutions',
company: 'Ritter Digital GmbH',
location: 'Oberhausen, Germany',
role: 'Fullstack Developer & AI Specialist',
company: 'CoderConda',
location: 'Cologne, Germany',
email: 'info@damjan-savic.com',
website: 'www.damjan-savic.com',
languages: [
@@ -50,21 +59,31 @@ export const meta = {
},
expertise: {
areas: [
'JTL Integration & Consulting',
'Python Backend Development',
'JavaScript/TypeScript Frontend',
'AI/ML Integration with OLLAMA',
'Process Automation',
'E-Commerce Development',
'System Integration',
'ERP Systems',
'Digital Transformation'
'ERP System Integration',
'Electron Desktop Apps',
'React & Next.js Applications'
],
technologies: [
'Python',
'React/Next.js',
'MariaDB',
'JavaScript',
'TypeScript',
'React',
'Next.js',
'Node.js',
'OLLAMA',
'Electron',
'FastAPI',
'Django',
'PostgreSQL',
'MongoDB',
'Docker',
'JTL-WaWi',
'Shopify',
'Shopware'
'Kubernetes',
'AWS'
]
},
seo: {
@@ -75,12 +94,12 @@ export const meta = {
og: {
type: 'website',
locale: 'en_US',
siteName: 'Damjan Savić - Digital Solutions',
siteName: 'Damjan Savić - Fullstack Developer Portfolio',
images: {
url: '/images/og-image.jpg',
width: 1200,
height: 630,
alt: 'Damjan Savić - Digital Solutions & JTL Integration'
alt: 'Damjan Savić - Fullstack Developer | Python, JavaScript, AI/OLLAMA'
}
},
twitter: {
+94
View File
@@ -0,0 +1,94 @@
export const seoContent = {
hero: {
title: "Damjan Savić - Fullstack Developer",
subtitle: "Python, JavaScript & AI Specialist",
description: "Building modern web applications, automation solutions, and AI integration with OLLAMA",
cta: {
primary: "View Projects",
secondary: "Get in Touch"
}
},
services: {
title: "My Expertise",
subtitle: "Specialized in modern technologies and solutions",
items: {
backend: {
title: "Backend Development with Python & Node.js",
description: "Scalable APIs and microservices with FastAPI, Django, and Express. Focus on performance and clean architecture.",
keywords: ["Python Backend", "FastAPI", "Django", "Node.js", "REST API", "GraphQL"]
},
frontend: {
title: "Frontend Development with React & Next.js",
description: "Modern, responsive web applications with React, Next.js, and TypeScript. SEO-optimized and performant.",
keywords: ["React", "Next.js", "TypeScript", "Tailwind CSS", "Redux", "SPA"]
},
ai: {
title: "AI/ML Integration with OLLAMA",
description: "Local AI solutions with OLLAMA for privacy and cost efficiency. Custom LLM integration for your applications.",
keywords: ["OLLAMA", "AI Integration", "Machine Learning", "LLM", "AI Development", "NLP"]
},
erp: {
title: "ERP System Integration & Development",
description: "Seamless integration of ERP systems. Custom modules and business process automation.",
keywords: ["ERP Integration", "SAP", "Odoo", "Business Automation", "System Integration"]
},
ecommerce: {
title: "E-Commerce & Online Shop Solutions",
description: "Development and optimization of e-commerce platforms. Performance optimization and conversion improvement.",
keywords: ["E-Commerce", "Shopify", "WooCommerce", "Payment Integration", "Shop Systems"]
},
automation: {
title: "Process Automation & Workflow Optimization",
description: "Automating repetitive tasks with Python. RPA and custom automation tools for efficiency gains.",
keywords: ["Process Automation", "Python Automation", "RPA", "Workflow", "Selenium", "Task Automation"]
}
}
},
skills: {
title: "Technology Stack",
categories: {
languages: {
title: "Programming Languages",
items: ["Python", "JavaScript", "TypeScript", "SQL", "Bash"]
},
backend: {
title: "Backend Technologies",
items: ["FastAPI", "Django", "Node.js", "Express", "PostgreSQL", "MongoDB", "Redis"]
},
frontend: {
title: "Frontend Technologies",
items: ["React", "Next.js", "Vue.js", "Tailwind CSS", "Material-UI", "Framer Motion"]
},
ai: {
title: "AI/ML Tools",
items: ["OLLAMA", "LangChain", "OpenAI API", "Hugging Face", "TensorFlow", "PyTorch"]
},
devops: {
title: "DevOps & Tools",
items: ["Docker", "Kubernetes", "AWS", "CI/CD", "Git", "Linux"]
}
}
},
cta: {
title: "Ready for Your Next Project?",
description: "Let's turn your ideas into reality together. From concept to implementation.",
button: "Schedule Free Consultation"
},
testimonials: {
title: "What Clients Say",
items: [
{
text: "Damjan transformed our Python scripts into a modern web app. The automation saves us 4 hours of work daily.",
author: "Michael Schmidt",
role: "CTO, TechStart GmbH",
keywords: ["Python Automation", "Web App Development"]
},
{
text: "The OLLAMA integration enables AI features without cloud costs. Privacy and performance are first-class.",
author: "Sarah Weber",
role: "Product Owner, DataSec AG",
keywords: ["OLLAMA Integration", "Privacy AI"]
}
]
}
}
+6 -1
View File
@@ -3,8 +3,10 @@ import { createRoot } from 'react-dom/client';
import { HelmetProvider } from 'react-helmet-async';
import App from './App';
import './index.css';
import './styles/mobile-optimizations.css';
import './i18n';
import { register as registerServiceWorker } from './utils/serviceWorkerRegistration';
import { reportWebVitals } from './utils/webVitals';
// Typdefinition für Google Analytics
declare global {
@@ -28,4 +30,7 @@ createRoot(rootElement).render(
);
// Register service worker
registerServiceWorker();
registerServiceWorker();
// Report Web Vitals
reportWebVitals();
+3
View File
@@ -6,6 +6,7 @@ import Skills from './components/Skills';
import Workflow from './components/Workflow';
import Journey from './components/Journey';
import { generateSchema } from './components/Schema';
import { PersonSchema } from '../../components/schemas/PersonSchema';
const AboutPage = () => {
const { t } = useTranslation();
@@ -22,6 +23,8 @@ const AboutPage = () => {
description={t('pages.about.seo.description')}
schema={generateSchema(t)}
/>
<PersonSchema />
<div className="relative min-h-screen">
{/* Hero Section */}
+6
View File
@@ -5,6 +5,7 @@ import Experience from './components/Experience';
import Skills from './components/Skills';
import Projects from './components/Projects';
import About from './components/About';
import { FAQSection } from '../../components/FAQSection';
const HomePage = () => {
const schema = {
@@ -88,6 +89,11 @@ const HomePage = () => {
<div id="about">
<About />
</div>
{/* FAQ Section */}
<div id="faq" className="py-16">
<FAQSection />
</div>
</main>
</PageTransition>
);
+174
View File
@@ -0,0 +1,174 @@
/* Mobile-first responsive design */
.portfolio-grid {
display: grid;
grid-template-columns: 1fr;
gap: 1.5rem;
padding: 1rem;
}
@media (min-width: 768px) {
.portfolio-grid {
grid-template-columns: repeat(2, 1fr);
gap: 2rem;
padding: 2rem;
}
}
@media (min-width: 1024px) {
.portfolio-grid {
grid-template-columns: repeat(3, 1fr);
gap: 2.5rem;
padding: 3rem;
}
}
/* Touch-friendly interactive elements */
.btn,
button,
a {
min-height: 44px;
min-width: 44px;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 12px 24px;
touch-action: manipulation;
-webkit-tap-highlight-color: transparent;
}
/* Mobile navigation optimized for thumb reach */
.mobile-nav {
position: fixed;
bottom: 0;
left: 0;
right: 0;
display: none;
background: var(--nav-bg, #18181B);
border-top: 1px solid var(--border-color, #27272A);
z-index: 50;
padding-bottom: env(safe-area-inset-bottom);
}
@media (max-width: 767px) {
.mobile-nav {
display: flex;
justify-content: space-around;
padding: 0.5rem;
}
.desktop-nav {
display: none;
}
}
.mobile-nav-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
padding: 8px 16px;
color: var(--text-secondary, #A1A1AA);
text-decoration: none;
font-size: 12px;
transition: color 0.2s;
}
.mobile-nav-item:active,
.mobile-nav-item.active {
color: var(--text-primary, #FFFFFF);
}
.mobile-nav-item svg {
width: 24px;
height: 24px;
}
/* Improved touch targets */
.clickable-card {
position: relative;
cursor: pointer;
}
.clickable-card::after {
content: '';
position: absolute;
inset: -8px;
z-index: -1;
}
/* Optimized scrolling */
.scroll-container {
-webkit-overflow-scrolling: touch;
scroll-snap-type: x mandatory;
scroll-behavior: smooth;
}
.scroll-item {
scroll-snap-align: start;
scroll-snap-stop: always;
}
/* Prevent text selection on interactive elements */
.no-select {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/* Optimized font sizes for mobile */
@media (max-width: 640px) {
h1 {
font-size: 2rem;
line-height: 1.2;
}
h2 {
font-size: 1.5rem;
line-height: 1.3;
}
h3 {
font-size: 1.25rem;
line-height: 1.4;
}
body, p {
font-size: 16px;
line-height: 1.6;
}
}
/* Safe area handling for modern phones */
.safe-area-padding {
padding-left: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right);
padding-top: env(safe-area-inset-top);
padding-bottom: env(safe-area-inset-bottom);
}
/* Performance optimizations */
.will-change-transform {
will-change: transform;
}
.gpu-accelerated {
transform: translateZ(0);
-webkit-transform: translateZ(0);
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
perspective: 1000;
-webkit-perspective: 1000;
}
/* Reduced motion for accessibility */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
+72
View File
@@ -0,0 +1,72 @@
import { onCLS, onINP, onLCP, onFCP, onTTFB } from 'web-vitals';
interface Metric {
name: string;
value: number;
rating: 'good' | 'needs-improvement' | 'poor';
delta: number;
id: string;
}
function sendToAnalytics(metric: Metric) {
const body = JSON.stringify({
name: metric.name,
value: metric.value,
rating: metric.rating,
delta: metric.delta,
id: metric.id
});
// Send to Google Analytics
if (window.gtag) {
window.gtag('event', metric.name, {
event_category: 'Web Vitals',
event_label: metric.id,
value: Math.round(metric.name === 'CLS' ? metric.value * 1000 : metric.value),
non_interaction: true,
metric_rating: metric.rating,
metric_delta: metric.delta
});
}
// Dispatch custom event for Performance Monitor
window.dispatchEvent(new CustomEvent('web-vitals', {
detail: {
name: metric.name,
value: metric.value,
rating: metric.rating,
timestamp: Date.now()
}
}));
// Also send to your analytics endpoint if needed
if (navigator.sendBeacon) {
navigator.sendBeacon('/api/analytics', body);
}
}
export function reportWebVitals() {
onCLS(sendToAnalytics);
onINP(sendToAnalytics);
onLCP(sendToAnalytics);
onFCP(sendToAnalytics);
onTTFB(sendToAnalytics);
}
// Helper function to get Web Vitals score
export function getWebVitalsScore(metric: Metric): string {
const thresholds = {
LCP: { good: 2500, poor: 4000 },
INP: { good: 200, poor: 500 },
CLS: { good: 0.1, poor: 0.25 },
FCP: { good: 1800, poor: 3000 },
TTFB: { good: 800, poor: 1800 }
};
const threshold = thresholds[metric.name as keyof typeof thresholds];
if (!threshold) return 'unknown';
if (metric.value <= threshold.good) return 'good';
if (metric.value <= threshold.poor) return 'needs-improvement';
return 'poor';
}