From 142e58288639d9adebd82c74385908b52b9f4172 Mon Sep 17 00:00:00 2001 From: Damjan Savic Date: Sun, 25 Jan 2026 06:29:15 +0100 Subject: [PATCH] auto-claude: subtask-2-2 - Delete src/pages-vite directory (454KB, 50 files) --- .auto-claude-status | 6 +- src/pages-vite/DashboardPage.tsx | 233 -------- src/pages-vite/ImprintPage.tsx | 93 ---- src/pages-vite/LoginPage.tsx | 99 ---- src/pages-vite/NotFoundPage.tsx | 40 -- src/pages-vite/PrivacyPolicyPage.tsx | 199 ------- src/pages-vite/TermsPage.tsx | 81 --- src/pages-vite/about/components/Hero.tsx | 112 ---- src/pages-vite/about/components/Journey.tsx | 156 ------ src/pages-vite/about/components/Schema.tsx | 51 -- src/pages-vite/about/components/SkillBar.tsx | 46 -- .../about/components/SkillGroup.tsx | 38 -- src/pages-vite/about/components/Skills.tsx | 139 ----- src/pages-vite/about/components/Workflow.tsx | 112 ---- src/pages-vite/about/index.tsx | 96 ---- src/pages-vite/blog/components/BlogPost.tsx | 519 ------------------ .../blog/components/BlogpostCard.tsx | 196 ------- src/pages-vite/blog/index.tsx | 185 ------- .../blog/posts/erp-integration-breuninger.mdx | 229 -------- .../fullstack-development-timetracking.mdx | 303 ---------- src/pages-vite/blog/posts/rfid-automation.mdx | 231 -------- src/pages-vite/blog/utils/mdxLoader.ts | 46 -- .../contact/components/ContactForm.tsx | 235 -------- .../contact/components/ContactInfo.tsx | 137 ----- src/pages-vite/contact/index.tsx | 109 ---- src/pages-vite/home/components/About.tsx | 134 ----- src/pages-vite/home/components/Experience.tsx | 205 ------- src/pages-vite/home/components/Expertise.tsx | 90 --- src/pages-vite/home/components/Hero.tsx | 158 ------ src/pages-vite/home/components/Projects.tsx | 96 ---- src/pages-vite/home/components/Skills.tsx | 182 ------ src/pages-vite/home/index.tsx | 115 ---- src/pages-vite/portfolio/ProjectPage.tsx | 417 -------------- src/pages-vite/portfolio/ProjectsData.tsx | 86 --- src/pages-vite/portfolio/TranslatedMDX.tsx | 16 - src/pages-vite/portfolio/[slug].tsx | 61 -- .../portfolio/components/PortfolioCard.tsx | 169 ------ .../portfolio/components/PortfolioGrid.tsx | 145 ----- .../portfolio/components/ProjectContent.tsx | 63 --- .../portfolio/components/ProjectLayout.tsx | 121 ---- src/pages-vite/portfolio/index.tsx | 62 --- .../portfolio/projects/ai-data-reader.mdx | 154 ------ .../projects/ai-music-production.mdx | 132 ----- .../projects/automated-ad-creatives.mdx | 146 ----- .../portfolio/projects/cursor-ide.mdx | 155 ------ .../portfolio/projects/kamenpro.mdx | 229 -------- .../projects/power-platform-governance.mdx | 320 ----------- .../portfolio/projects/smart-warehouse.mdx | 270 --------- .../portfolio/projects/website-mit-ki.mdx | 168 ------ src/pages-vite/portfolio/types.ts | 18 - src/pages-vite/portfolio/utils.ts | 205 ------- 51 files changed, 3 insertions(+), 7605 deletions(-) delete mode 100644 src/pages-vite/DashboardPage.tsx delete mode 100644 src/pages-vite/ImprintPage.tsx delete mode 100644 src/pages-vite/LoginPage.tsx delete mode 100644 src/pages-vite/NotFoundPage.tsx delete mode 100644 src/pages-vite/PrivacyPolicyPage.tsx delete mode 100644 src/pages-vite/TermsPage.tsx delete mode 100644 src/pages-vite/about/components/Hero.tsx delete mode 100644 src/pages-vite/about/components/Journey.tsx delete mode 100644 src/pages-vite/about/components/Schema.tsx delete mode 100644 src/pages-vite/about/components/SkillBar.tsx delete mode 100644 src/pages-vite/about/components/SkillGroup.tsx delete mode 100644 src/pages-vite/about/components/Skills.tsx delete mode 100644 src/pages-vite/about/components/Workflow.tsx delete mode 100644 src/pages-vite/about/index.tsx delete mode 100644 src/pages-vite/blog/components/BlogPost.tsx delete mode 100644 src/pages-vite/blog/components/BlogpostCard.tsx delete mode 100644 src/pages-vite/blog/index.tsx delete mode 100644 src/pages-vite/blog/posts/erp-integration-breuninger.mdx delete mode 100644 src/pages-vite/blog/posts/fullstack-development-timetracking.mdx delete mode 100644 src/pages-vite/blog/posts/rfid-automation.mdx delete mode 100644 src/pages-vite/blog/utils/mdxLoader.ts delete mode 100644 src/pages-vite/contact/components/ContactForm.tsx delete mode 100644 src/pages-vite/contact/components/ContactInfo.tsx delete mode 100644 src/pages-vite/contact/index.tsx delete mode 100644 src/pages-vite/home/components/About.tsx delete mode 100644 src/pages-vite/home/components/Experience.tsx delete mode 100644 src/pages-vite/home/components/Expertise.tsx delete mode 100644 src/pages-vite/home/components/Hero.tsx delete mode 100644 src/pages-vite/home/components/Projects.tsx delete mode 100644 src/pages-vite/home/components/Skills.tsx delete mode 100644 src/pages-vite/home/index.tsx delete mode 100644 src/pages-vite/portfolio/ProjectPage.tsx delete mode 100644 src/pages-vite/portfolio/ProjectsData.tsx delete mode 100644 src/pages-vite/portfolio/TranslatedMDX.tsx delete mode 100644 src/pages-vite/portfolio/[slug].tsx delete mode 100644 src/pages-vite/portfolio/components/PortfolioCard.tsx delete mode 100644 src/pages-vite/portfolio/components/PortfolioGrid.tsx delete mode 100644 src/pages-vite/portfolio/components/ProjectContent.tsx delete mode 100644 src/pages-vite/portfolio/components/ProjectLayout.tsx delete mode 100644 src/pages-vite/portfolio/index.tsx delete mode 100644 src/pages-vite/portfolio/projects/ai-data-reader.mdx delete mode 100644 src/pages-vite/portfolio/projects/ai-music-production.mdx delete mode 100644 src/pages-vite/portfolio/projects/automated-ad-creatives.mdx delete mode 100644 src/pages-vite/portfolio/projects/cursor-ide.mdx delete mode 100644 src/pages-vite/portfolio/projects/kamenpro.mdx delete mode 100644 src/pages-vite/portfolio/projects/power-platform-governance.mdx delete mode 100644 src/pages-vite/portfolio/projects/smart-warehouse.mdx delete mode 100644 src/pages-vite/portfolio/projects/website-mit-ki.mdx delete mode 100644 src/pages-vite/portfolio/types.ts delete mode 100644 src/pages-vite/portfolio/utils.ts diff --git a/.auto-claude-status b/.auto-claude-status index 73299f4..407bb31 100644 --- a/.auto-claude-status +++ b/.auto-claude-status @@ -3,7 +3,7 @@ "spec": "029-remove-dead-code-components-vite-and-pages-vite-fo", "state": "building", "subtasks": { - "completed": 2, + "completed": 3, "total": 8, "in_progress": 1, "failed": 0 @@ -18,8 +18,8 @@ "max": 1 }, "session": { - "number": 4, + "number": 5, "started_at": "2026-01-25T06:18:19.396966" }, - "last_update": "2026-01-25T06:27:40.436575" + "last_update": "2026-01-25T06:28:41.216540" } \ No newline at end of file diff --git a/src/pages-vite/DashboardPage.tsx b/src/pages-vite/DashboardPage.tsx deleted file mode 100644 index 858868c..0000000 --- a/src/pages-vite/DashboardPage.tsx +++ /dev/null @@ -1,233 +0,0 @@ -import { useEffect, useState } from 'react'; -import { useNavigate } from 'react-router-dom'; -import { supabase } from '../utils/supabaseClient'; -import { Users, Eye, Clock, TrendingUp, MousePointer, ArrowUpRight, ArrowDownRight } from 'lucide-react'; -import SEO from '../components/SEO'; -import PageTransition from '../components/PageTransition'; - -interface AnalyticsData { - pageViews: number; - uniqueVisitors: number; - avgTimeOnSite: string; - bounceRate: string; - conversions: number; - conversionRate: string; - activeUsers: number; - topPages: Array<{ - path: string; - views: number; - change: number; - }>; - realtimeStats: { - currentVisitors: number; - pagesPerMinute: number; - }; - events: Array<{ - name: string; - count: number; - trend: number; - }>; -} - -const DashboardPage = () => { - const navigate = useNavigate(); - const [loading, setLoading] = useState(true); - const [analyticsData, setAnalyticsData] = useState({ - pageViews: 0, - uniqueVisitors: 0, - avgTimeOnSite: '0:00', - bounceRate: '0%', - conversions: 0, - conversionRate: '0%', - activeUsers: 0, - topPages: [], - realtimeStats: { - currentVisitors: 0, - pagesPerMinute: 0 - }, - events: [] - }); - - useEffect(() => { - checkAuth(); - fetchAnalyticsData(); - const interval = setInterval(fetchRealtimeData, 30000); // Update every 30 seconds - - return () => clearInterval(interval); - }, []); - - const checkAuth = async () => { - const { data: { session } } = await supabase.auth.getSession(); - if (!session) { - navigate('/login'); - } - setLoading(false); - }; - - const fetchAnalyticsData = async () => { - // Simulated data - replace with actual GA4 API calls - setAnalyticsData({ - pageViews: 1234, - uniqueVisitors: 567, - avgTimeOnSite: '2:45', - bounceRate: '45%', - conversions: 89, - conversionRate: '15.7%', - activeUsers: 23, - topPages: [ - { path: '/', views: 450, change: 5.2 }, - { path: '/blog', views: 320, change: -2.1 }, - { path: '/portfolio', views: 280, change: 8.4 }, - { path: '/contact', views: 184, change: 3.7 } - ], - realtimeStats: { - currentVisitors: 23, - pagesPerMinute: 4.2 - }, - events: [ - { name: 'Contact Form Submit', count: 45, trend: 12.3 }, - { name: 'Portfolio View', count: 156, trend: -4.2 }, - { name: 'CV Download', count: 78, trend: 8.7 }, - { name: 'Blog Read', count: 234, trend: 15.4 } - ] - }); - }; - - const fetchRealtimeData = async () => { - // Simulated real-time data update - setAnalyticsData(prev => ({ - ...prev, - realtimeStats: { - currentVisitors: Math.floor(Math.random() * 30) + 15, - pagesPerMinute: +(Math.random() * 5 + 2).toFixed(1) - } - })); - }; - - if (loading) { - return ( -
-
-
- ); - } - - return ( - - -
-
-
-

Analytics Dashboard

-
-
- - {analyticsData.realtimeStats.currentVisitors} active users - -
-
- -
-
-
-

Page Views

- -
-

{analyticsData.pageViews}

-

- {analyticsData.realtimeStats.pagesPerMinute} per minute -

-
- -
-
-

Unique Visitors

- -
-

{analyticsData.uniqueVisitors}

-

- {analyticsData.activeUsers} currently active -

-
- -
-
-

Conversions

- -
-

{analyticsData.conversions}

-

- {analyticsData.conversionRate} conversion rate -

-
- -
-
-

Avg. Time on Site

- -
-

{analyticsData.avgTimeOnSite}

-

- {analyticsData.bounceRate} bounce rate -

-
-
- -
-
-

Top Pages

-
- {analyticsData.topPages.map((page, index) => ( -
-
- - {page.path} -
-
- {page.views} -
= 0 ? 'text-green-500' : 'text-red-500'}`}> - {page.change >= 0 ? ( - - ) : ( - - )} - {Math.abs(page.change)}% -
-
-
- ))} -
-
- -
-

Event Tracking

-
- {analyticsData.events.map((event, index) => ( -
- {event.name} -
- {event.count} -
= 0 ? 'text-green-500' : 'text-red-500'}`}> - {event.trend >= 0 ? ( - - ) : ( - - )} - {Math.abs(event.trend)}% -
-
-
- ))} -
-
-
-
-
-
- ); -}; - -export default DashboardPage; \ No newline at end of file diff --git a/src/pages-vite/ImprintPage.tsx b/src/pages-vite/ImprintPage.tsx deleted file mode 100644 index b6eb0f4..0000000 --- a/src/pages-vite/ImprintPage.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { useTranslation } from 'react-i18next'; -import SEO from '../components/SEO'; -import PageTransition from '../components/PageTransition'; -import Breadcrumbs from '../components/Breadcrumbs'; - -const ImprintPage = () => { - const { t } = useTranslation(); - - const schema = { - '@context': 'https://schema.org', - '@type': 'WebPage', - name: 'Impressum', - description: 'Impressum für die Portfolio-Website von Damjan Savić' - }; - - return ( - - -
-
- - -

Impressum

- -
-
-

Kontakt

-
-

Damjan Savić

-

Fullstack Developer & Digital Solutions Consultant

- -

E-Mail:

-

info@damjan-savic.com

-

Website: https://damjan-savic.com

-
-
- -
-

Haftungsausschluss

- -

Haftung für Inhalte

-

Die Inhalte meiner Seiten wurden mit größter Sorgfalt erstellt. Für die Richtigkeit, Vollständigkeit und Aktualität der Inhalte kann ich jedoch keine Gewähr übernehmen.

- -

Haftung für Links

-

Meine Website enthält Links zu externen Websites Dritter, auf deren Inhalte ich keinen Einfluss habe. Deshalb kann ich für diese fremden Inhalte auch keine Gewähr übernehmen. Für die Inhalte der verlinkten Seiten ist stets der jeweilige Anbieter oder Betreiber der Seiten verantwortlich.

-
- -
-

Urheberrecht

-

Die durch den Seitenbetreiber erstellten Inhalte und Werke auf diesen Seiten unterliegen dem deutschen Urheberrecht. Die Vervielfältigung, Bearbeitung, Verbreitung und jede Art der Verwertung außerhalb der Grenzen des Urheberrechtes bedürfen der schriftlichen Zustimmung des jeweiligen Autors bzw. Erstellers.

-

Soweit die Inhalte auf dieser Seite nicht vom Betreiber erstellt wurden, werden die Urheberrechte Dritter beachtet. Sollten Sie trotzdem auf eine Urheberrechtsverletzung aufmerksam werden, bitte ich um einen entsprechenden Hinweis.

-
- -
-

Bildnachweise

-

Die auf dieser Website verwendeten Bilder und Grafiken stammen aus folgenden Quellen:

-
    -
  • Eigene Aufnahmen und Erstellungen
  • -
  • Icons: Heroicons, Tabler Icons (MIT Lizenz)
  • -
  • Weitere Bildquellen sind direkt bei den jeweiligen Bildern angegeben
  • -
-
- -
-

Datenschutz

-

Die Nutzung unserer Webseite ist in der Regel ohne Angabe personenbezogener Daten möglich. Soweit auf unseren Seiten personenbezogene Daten (beispielsweise Name, Anschrift oder E-Mail-Adressen) erhoben werden, erfolgt dies, soweit möglich, stets auf freiwilliger Basis. Diese Daten werden ohne Ihre ausdrückliche Zustimmung nicht an Dritte weitergegeben.

-

Weitere Informationen zum Datenschutz finden Sie in unserer Datenschutzerklärung.

-
- -
-

Technische Umsetzung

-
-

Design & Entwicklung: Damjan Savić

-

Technologien: React, TypeScript, Tailwind CSS

-
-
- -
-

Copyright

-

© {new Date().getFullYear()} Damjan Savić. Alle Rechte vorbehalten.

-
-
-
-
-
- ); -}; - -export default ImprintPage; \ No newline at end of file diff --git a/src/pages-vite/LoginPage.tsx b/src/pages-vite/LoginPage.tsx deleted file mode 100644 index 8f7bba5..0000000 --- a/src/pages-vite/LoginPage.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import React, { useState } from 'react'; -import { useNavigate } from 'react-router-dom'; -import { supabase } from '../utils/supabaseClient'; -import { Lock } from 'lucide-react'; -import SEO from '../components/SEO'; -import PageTransition from '../components/PageTransition'; - -const LoginPage = () => { - const navigate = useNavigate(); - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(null); - - const handleLogin = async (e: React.FormEvent) => { - e.preventDefault(); - setLoading(true); - setError(null); - - try { - const { error } = await supabase.auth.signInWithPassword({ - email, - password - }); - - if (error) throw error; - navigate('/dashboard'); - } catch (err) { - setError(err instanceof Error ? err.message : 'An error occurred'); - } finally { - setLoading(false); - } - }; - - return ( - - -
-
-
- -

Sign in to Dashboard

-
- -
- {error && ( -
- {error} -
- )} - -
-
- - setEmail(e.target.value)} - className="w-full bg-gray-900/50 border border-white/10 rounded-lg px-4 py-3 text-white placeholder-white/40 focus:outline-none focus:ring-2 focus:ring-orange-500" - /> -
- -
- - setPassword(e.target.value)} - className="w-full bg-gray-900/50 border border-white/10 rounded-lg px-4 py-3 text-white placeholder-white/40 focus:outline-none focus:ring-2 focus:ring-orange-500" - /> -
-
- - -
-
-
-
- ); -}; - -export default LoginPage; \ No newline at end of file diff --git a/src/pages-vite/NotFoundPage.tsx b/src/pages-vite/NotFoundPage.tsx deleted file mode 100644 index 1fa8890..0000000 --- a/src/pages-vite/NotFoundPage.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { useNavigate } from 'react-router-dom'; -import { FileQuestion } from 'lucide-react'; -import SEO from '../components/SEO'; - -const NotFoundPage = () => { - const navigate = useNavigate(); - - return ( - <> - -
-
- -

404

-

Page Not Found

-

The page you're looking for doesn't exist or has been moved.

-
- - -
-
-
- - ); -}; - -export default NotFoundPage \ No newline at end of file diff --git a/src/pages-vite/PrivacyPolicyPage.tsx b/src/pages-vite/PrivacyPolicyPage.tsx deleted file mode 100644 index 1f87b10..0000000 --- a/src/pages-vite/PrivacyPolicyPage.tsx +++ /dev/null @@ -1,199 +0,0 @@ -import { useTranslation } from 'react-i18next'; -import SEO from '../components/SEO'; -import PageTransition from '../components/PageTransition'; -import Breadcrumbs from '../components/Breadcrumbs'; - -const PrivacyPolicyPage = () => { - const { t } = useTranslation(); - - const schema = { - '@context': 'https://schema.org', - '@type': 'WebPage', - name: 'Datenschutzerklärung', - description: 'Datenschutzerklärung für die Portfolio-Website von Damjan Savić' - }; - - return ( - - -
-
- - -

Datenschutzerklärung

- -
-

Stand: {new Date().toLocaleDateString('de-DE', { year: 'numeric', month: 'long', day: 'numeric' })}

- -
-

1. Verantwortlicher

-

Verantwortlich für die Datenverarbeitung auf dieser Website im Sinne der Datenschutz-Grundverordnung (DSGVO) ist:

-
-

Damjan Savić

-

Köln, Deutschland

-

E-Mail: contact@damjan-savic.com

-

Telefon: +49 151 74477788

-
-
- -
-

2. Allgemeine Hinweise

-

Der Schutz Ihrer persönlichen Daten ist mir ein besonderes Anliegen. Ich verarbeite Ihre Daten daher ausschließlich auf Grundlage der gesetzlichen Bestimmungen (DSGVO, TKG 2003). In dieser Datenschutzerklärung informiere ich Sie über die wichtigsten Aspekte der Datenverarbeitung im Rahmen meiner Website.

- -

2.1 Datenminimierung

-

Ich erhebe nur solche personenbezogenen Daten, die für die Erfüllung der jeweiligen Zwecke erforderlich sind. Eine darüber hinausgehende Datenerhebung findet nicht statt.

- -

2.2 Rechtsgrundlagen

-

Die Verarbeitung Ihrer Daten erfolgt auf Basis folgender Rechtsgrundlagen:

-
    -
  • Art. 6 Abs. 1 lit. a DSGVO (Einwilligung)
  • -
  • Art. 6 Abs. 1 lit. b DSGVO (Vertragserfüllung)
  • -
  • Art. 6 Abs. 1 lit. f DSGVO (berechtigte Interessen)
  • -
-
- -
-

3. Datenerfassung auf dieser Website

- -

3.1 Cookies

-

Diese Website verwendet Cookies. Dabei handelt es sich um kleine Textdateien, die Ihr Webbrowser auf Ihrem Endgerät speichert. Cookies helfen dabei, unser Angebot nutzerfreundlicher, effektiver und sicherer zu machen.

- -

Folgende Arten von Cookies werden verwendet:

-
    -
  • Notwendige Cookies: Diese Cookies sind für den Betrieb der Website unerlässlich (z.B. Session-Cookies)
  • -
  • Funktionale Cookies: Diese Cookies ermöglichen erweiterte Funktionen (z.B. Spracheinstellungen)
  • -
  • Analyse-Cookies: Diese Cookies helfen uns, die Nutzung unserer Website zu verstehen (nur mit Ihrer Zustimmung)
  • -
  • Marketing-Cookies: Diese Cookies werden für personalisierte Werbung verwendet (nur mit Ihrer Zustimmung)
  • -
- -

Sie können Ihre Cookie-Einstellungen jederzeit über den Cookie-Banner auf unserer Website anpassen.

- -

3.2 Server-Log-Dateien

-

Der Provider der Seiten erhebt und speichert automatisch Informationen in so genannten Server-Log-Dateien, die Ihr Browser automatisch übermittelt. Dies sind:

-
    -
  • Browsertyp und Browserversion
  • -
  • Verwendetes Betriebssystem
  • -
  • Referrer URL
  • -
  • Hostname des zugreifenden Rechners
  • -
  • Uhrzeit der Serveranfrage
  • -
  • IP-Adresse (anonymisiert)
  • -
-

Diese Daten sind nicht bestimmten Personen zuordenbar. Eine Zusammenführung dieser Daten mit anderen Datenquellen wird nicht vorgenommen. Die Daten werden nach 7 Tagen automatisch gelöscht.

- -

3.3 Kontaktformular

-

Wenn Sie uns per Kontaktformular Anfragen zukommen lassen, werden Ihre Angaben aus dem Anfrageformular inklusive der von Ihnen dort angegebenen Kontaktdaten zwecks Bearbeitung der Anfrage und für den Fall von Anschlussfragen bei uns gespeichert.

-

Verarbeitete Daten:

-
    -
  • Name
  • -
  • E-Mail-Adresse
  • -
  • Betreff
  • -
  • Nachrichteninhalt
  • -
  • Datum und Uhrzeit der Anfrage
  • -
-

Rechtsgrundlage: Die Verarbeitung erfolgt auf Grundlage von Art. 6 Abs. 1 lit. b DSGVO (Vertragsanbahnung) oder Art. 6 Abs. 1 lit. f DSGVO (berechtigtes Interesse an der Beantwortung Ihrer Anfrage).

-

Speicherdauer: Die Daten werden gelöscht, sobald sie für die Erreichung des Zweckes ihrer Erhebung nicht mehr erforderlich sind, spätestens jedoch nach 6 Monaten.

-
- -
-

4. Externe Dienste und Inhalte

- -

4.1 Google Analytics

-

Diese Website benutzt Google Analytics, einen Webanalysedienst der Google LLC ("Google"). Google Analytics wird nur aktiviert, wenn Sie der Verwendung von Analyse-Cookies ausdrücklich zugestimmt haben.

-

Verarbeitete Daten:

-
    -
  • Anonymisierte IP-Adresse
  • -
  • Geräteinformationen
  • -
  • Browserinformationen
  • -
  • Nutzungsverhalten (besuchte Seiten, Verweildauer, etc.)
  • -
-

Zweck: Analyse der Websitenutzung zur Verbesserung unseres Angebots

-

Rechtsgrundlage: Art. 6 Abs. 1 lit. a DSGVO (Einwilligung)

-

Speicherdauer: 14 Monate

-

Sie können die Speicherung der Cookies durch eine entsprechende Einstellung Ihrer Browser-Software verhindern oder Ihre Einwilligung jederzeit über unseren Cookie-Banner widerrufen.

- -

4.2 Google Fonts

-

Diese Website nutzt zur einheitlichen Darstellung von Schriftarten so genannte Google Fonts. Google Fonts werden nur geladen, wenn Sie der Verwendung funktionaler Cookies zugestimmt haben.

-

Verarbeitete Daten: IP-Adresse

-

Zweck: Einheitliche Darstellung von Schriftarten

-

Rechtsgrundlage: Art. 6 Abs. 1 lit. a DSGVO (Einwilligung)

- -

4.3 Facebook Pixel

-

Diese Website nutzt das Facebook-Pixel von Meta Platforms Inc. Das Facebook-Pixel wird nur aktiviert, wenn Sie der Verwendung von Marketing-Cookies ausdrücklich zugestimmt haben.

-

Verarbeitete Daten:

-
    -
  • Cookie-ID
  • -
  • Geräteinformationen
  • -
  • Nutzungsverhalten
  • -
-

Zweck: Remarketing und Conversion-Tracking

-

Rechtsgrundlage: Art. 6 Abs. 1 lit. a DSGVO (Einwilligung)

-
- -
-

5. Ihre Rechte

-

Nach der Datenschutz-Grundverordnung stehen Ihnen folgende Rechte zu:

- -

5.1 Auskunftsrecht (Art. 15 DSGVO)

-

Sie haben das Recht, Auskunft über Ihre bei uns gespeicherten personenbezogenen Daten zu erhalten.

- -

5.2 Recht auf Berichtigung (Art. 16 DSGVO)

-

Sie haben das Recht, unrichtige oder unvollständige personenbezogene Daten berichtigen zu lassen.

- -

5.3 Recht auf Löschung (Art. 17 DSGVO)

-

Sie haben das Recht, die Löschung Ihrer personenbezogenen Daten zu verlangen, sofern die gesetzlichen Voraussetzungen vorliegen.

- -

5.4 Recht auf Einschränkung der Verarbeitung (Art. 18 DSGVO)

-

Sie haben das Recht, die Einschränkung der Verarbeitung Ihrer personenbezogenen Daten zu verlangen.

- -

5.5 Recht auf Datenübertragbarkeit (Art. 20 DSGVO)

-

Sie haben das Recht, Ihre personenbezogenen Daten in einem strukturierten, gängigen und maschinenlesbaren Format zu erhalten.

- -

5.6 Widerspruchsrecht (Art. 21 DSGVO)

-

Sie haben das Recht, aus Gründen, die sich aus Ihrer besonderen Situation ergeben, jederzeit gegen die Verarbeitung Sie betreffender personenbezogener Daten Widerspruch einzulegen.

- -

5.7 Recht auf Widerruf der Einwilligung

-

Sie haben das Recht, Ihre einmal erteilte Einwilligung jederzeit zu widerrufen. Die Rechtmäßigkeit der aufgrund der Einwilligung bis zum Widerruf erfolgten Verarbeitung wird dadurch nicht berührt.

- -

5.8 Beschwerderecht bei der Aufsichtsbehörde

-

Sie haben das Recht, sich bei einer Datenschutz-Aufsichtsbehörde zu beschweren. Die für uns zuständige Aufsichtsbehörde ist:

-
-

Landesbeauftragte für Datenschutz und Informationsfreiheit Nordrhein-Westfalen

-

Postfach 20 04 44

-

40102 Düsseldorf

-

Telefon: 0211/38424-0

-

E-Mail: poststelle@ldi.nrw.de

-
-
- -
-

6. Datensicherheit

-

Ich verwende innerhalb des Website-Besuchs das verbreitete SSL-Verfahren (Secure Socket Layer) in Verbindung mit der jeweils höchsten Verschlüsselungsstufe, die von Ihrem Browser unterstützt wird. Ob eine einzelne Seite unseres Internetauftrittes verschlüsselt übertragen wird, erkennen Sie an der geschlossenen Darstellung des Schüssel- beziehungsweise Schloss-Symbols in der unteren Statusleiste Ihres Browsers.

-

Darüber hinaus bediene ich mich geeigneter technischer und organisatorischer Sicherheitsmaßnahmen, um Ihre Daten gegen zufällige oder vorsätzliche Manipulationen, teilweisen oder vollständigen Verlust, Zerstörung oder gegen den unbefugten Zugriff Dritter zu schützen.

-
- -
-

7. Aktualität und Änderung dieser Datenschutzerklärung

-

Diese Datenschutzerklärung ist aktuell gültig und hat den Stand {new Date().toLocaleDateString('de-DE', { year: 'numeric', month: 'long', day: 'numeric' })}.

-

Durch die Weiterentwicklung unserer Website und Angebote darüber oder aufgrund geänderter gesetzlicher beziehungsweise behördlicher Vorgaben kann es notwendig werden, diese Datenschutzerklärung zu ändern. Die jeweils aktuelle Datenschutzerklärung kann jederzeit auf der Website unter /privacy von Ihnen abgerufen und ausgedruckt werden.

-
- -
-

8. Kontakt

-

Für Fragen zum Datenschutz können Sie sich jederzeit an mich wenden:

-
-

E-Mail: privacy@damjan-savic.com

-

Telefon: +49 151 74477788

-
-
-
-
-
-
- ); -}; - -export default PrivacyPolicyPage; \ No newline at end of file diff --git a/src/pages-vite/TermsPage.tsx b/src/pages-vite/TermsPage.tsx deleted file mode 100644 index 701c41d..0000000 --- a/src/pages-vite/TermsPage.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import { useTranslation } from 'react-i18next'; -import SEO from '../components/SEO'; -import PageTransition from '../components/PageTransition'; -import Breadcrumbs from '../components/Breadcrumbs'; - -const TermsPage = () => { - const { t } = useTranslation(); - - const schema = { - '@context': 'https://schema.org', - '@type': 'WebPage', - name: 'Terms of Service', - description: 'Terms of Service for Damjan Savić\'s portfolio website' - }; - - return ( - - -
-
- - -

Terms of Service

- -
-

Last updated: March 10, 2024

- -
-

1. Agreement to Terms

-

By accessing our website, you agree to be bound by these Terms of Service and to comply with all applicable laws and regulations.

-
- -
-

2. Intellectual Property Rights

-

All content on this website, including but not limited to text, graphics, logos, images, and software, is the property of Damjan Savić and is protected by intellectual property laws.

-
- -
-

3. User Responsibilities

-

When using our website, you agree to:

-
    -
  • Provide accurate information
  • -
  • Use the website legally and ethically
  • -
  • Not attempt to gain unauthorized access
  • -
  • Not interfere with the website's operation
  • -
-
- -
-

4. Limitation of Liability

-

We shall not be liable for any indirect, incidental, special, consequential, or punitive damages resulting from your use of or inability to use the website.

-
- -
-

5. Changes to Terms

-

We reserve the right to modify these terms at any time. We will notify users of any material changes by posting the new Terms of Service on this page.

-
- -
-

6. Governing Law

-

These terms shall be governed by and construed in accordance with the laws of Germany, without regard to its conflict of law provisions.

-
- -
-

7. Contact Information

-

For any questions about these Terms of Service, please contact us at:

-

Email: legal@damjan-savic.com

-

Address: {t('contact.address')}

-
-
-
-
-
- ); -}; - -export default TermsPage; \ No newline at end of file diff --git a/src/pages-vite/about/components/Hero.tsx b/src/pages-vite/about/components/Hero.tsx deleted file mode 100644 index 764b7f6..0000000 --- a/src/pages-vite/about/components/Hero.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import { motion } from 'framer-motion'; -import { Terminal, Globe2, Code2 } from 'lucide-react'; -import { useTranslation } from 'react-i18next'; - -const Hero = () => { - const { t } = useTranslation(); - - // Sichere Abfrage der Sprachen - const getLanguages = () => { - try { - const languages = t('pages.about.hero.languages', { returnObjects: true }); - return typeof languages === 'object' ? Object.entries(languages) : []; - } catch (error) { - console.error('Error loading languages:', error); - return []; - } - }; - - return ( -
- {/* Main Content */} -
- {/* Title */} - - {t('pages.about.hero.title')} - - - {/* Description */} - -

- {t('pages.about.hero.description')} -

-
- - {/* Expertise Areas */} - -
- -

- {t('pages.about.hero.expertise.processAutomation.title')} -

-

- {t('pages.about.hero.expertise.processAutomation.description')} -

-
-
- -

- {t('pages.about.hero.expertise.systemIntegration.title')} -

-

- {t('pages.about.hero.expertise.systemIntegration.description')} -

-
-
- -

- {t('pages.about.hero.expertise.customDevelopment.title')} -

-

- {t('pages.about.hero.expertise.customDevelopment.description')} -

-
-
- - {/* Languages */} - - {getLanguages().map(([key, value]) => ( -
- {value} -
- ))} -
- - {/* About Me Link */} - - {t('pages.about.hero.aboutMe')} - -
-
- ); -}; - -export default Hero; \ No newline at end of file diff --git a/src/pages-vite/about/components/Journey.tsx b/src/pages-vite/about/components/Journey.tsx deleted file mode 100644 index bd75b77..0000000 --- a/src/pages-vite/about/components/Journey.tsx +++ /dev/null @@ -1,156 +0,0 @@ -import { motion } from 'framer-motion'; -import { useTranslation } from 'react-i18next'; -import { Briefcase, GraduationCap, Globe } from 'lucide-react'; - -interface WorkPosition { - title: string; - period: string; - company: string; - location: string; - highlights: string[]; -} - -const Journey = () => { - const { t } = useTranslation(); - - // Sichere Datenabruf-Funktionen - const getWorkPositions = (): WorkPosition[] => { - try { - const positions = t('pages.about.journey.work.positions', { returnObjects: true }); - return Array.isArray(positions) ? (positions as WorkPosition[]) : []; - } catch (error) { - console.error('Error loading work positions:', error); - return []; - } - }; - - const getLanguageItems = () => { - try { - const items = t('pages.about.journey.languages.items', { returnObjects: true }); - return typeof items === 'object' ? Object.entries(items) : []; - } catch (error) { - console.error('Error loading language items:', error); - return []; - } - }; - - return ( -
-
- {/* Section Header */} - -

- {t('pages.about.journey.title')} -

-

- {t('pages.about.journey.subtitle')} -

-
- -
- {/* Education Column */} - -
-
- -

- {t('pages.about.journey.education.title')} -

-
-
-
-

- {t('pages.about.journey.education.degrees.masters.degree')} -

-

- {t('pages.about.journey.education.degrees.masters.university')} -

-

- {t('pages.about.journey.education.degrees.masters.period')} -

-
-
-

- {t('pages.about.journey.education.degrees.bachelors.degree')} -

-

- {t('pages.about.journey.education.degrees.bachelors.university')} -

-

- {t('pages.about.journey.education.degrees.bachelors.period')} -

-
-
-
- - {/* Languages */} -
-
- -

- {t('pages.about.journey.languages.title')} -

-
-
- {getLanguageItems().map(([key, item]) => ( -
-
{item.name}
-
{item.level}
-
- ))} -
-
-
- - {/* Work History */} - - {getWorkPositions().map((job, index) => ( -
-
-

{job.title}

- {job.period} -
-
- -

{job.company}

- • {job.location} -
-
    - {job.highlights.map((highlight: string, idx: number) => ( -
  • - - {highlight} -
  • - ))} -
-
- ))} -
-
-
-
- ); -}; - -export default Journey; diff --git a/src/pages-vite/about/components/Schema.tsx b/src/pages-vite/about/components/Schema.tsx deleted file mode 100644 index 2d9e77a..0000000 --- a/src/pages-vite/about/components/Schema.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { useEffect } from 'react'; -import { useTranslation } from 'react-i18next'; - -// Schema Generator Funktion -export const generateSchema = (t: (key: string, options?: any) => string) => ({ - '@context': 'https://schema.org', - '@type': 'AboutPage', - name: t('schema.page.name'), - description: t('schema.page.description'), - mainEntity: { - '@type': 'Person', - name: 'Damjan Savić', - jobTitle: t('schema.person.jobTitle'), - description: t('schema.person.description'), - knowsLanguage: Object.values(t('schema.person.languages', { returnObjects: true })), - hasSkill: Object.values(t('schema.person.skills', { returnObjects: true })) - } -}); - -const Schema = () => { - const { t } = useTranslation(); - - useEffect(() => { - const schema = generateSchema(t); - - // Entferne vorhandenes Schema - const existingSchema = document.querySelector('script[data-schema="about"]'); - if (existingSchema) { - existingSchema.remove(); - } - - // Füge neues Schema hinzu - const script = document.createElement('script'); - script.setAttribute('type', 'application/ld+json'); - script.setAttribute('data-schema', 'about'); - script.textContent = JSON.stringify(schema); - document.head.appendChild(script); - - // Cleanup beim Unmount - return () => { - const script = document.querySelector('script[data-schema="about"]'); - if (script) { - script.remove(); - } - }; - }, [t]); - - return null; // Schema-Komponente rendert nichts sichtbares -}; - -export default Schema; \ No newline at end of file diff --git a/src/pages-vite/about/components/SkillBar.tsx b/src/pages-vite/about/components/SkillBar.tsx deleted file mode 100644 index 50298d7..0000000 --- a/src/pages-vite/about/components/SkillBar.tsx +++ /dev/null @@ -1,46 +0,0 @@ -// SkillBar.tsx -import React from 'react'; -import { useTranslation } from 'react-i18next'; -import { motion } from 'framer-motion'; - -interface SkillBarProps { - name: string; - level: number; - className?: string; - 'aria-label'?: string; -} - -const SkillBar: React.FC = ({ name, level, className = '', 'aria-label': ariaLabel }) => { - const { t } = useTranslation(); - - return ( -
-
- {name} - - {level}% - -
-
- -
-
- ); -}; - -export default SkillBar; \ No newline at end of file diff --git a/src/pages-vite/about/components/SkillGroup.tsx b/src/pages-vite/about/components/SkillGroup.tsx deleted file mode 100644 index 5be643c..0000000 --- a/src/pages-vite/about/components/SkillGroup.tsx +++ /dev/null @@ -1,38 +0,0 @@ -// SkillGroup.tsx -import { useTranslation } from 'react-i18next'; -import SkillBar from './SkillBar'; - -interface SkillGroupProps { - category: string; - items: { - name: string; - level: number; - }[]; - className?: string; -} - -const SkillGroup: React.FC = ({ category, items, className = '' }) => { - const { t } = useTranslation(); - - return ( -
-

{category}

-
- {items.map((skill) => ( - - ))} -
-
- ); -}; - -export default SkillGroup; \ No newline at end of file diff --git a/src/pages-vite/about/components/Skills.tsx b/src/pages-vite/about/components/Skills.tsx deleted file mode 100644 index 343ed27..0000000 --- a/src/pages-vite/about/components/Skills.tsx +++ /dev/null @@ -1,139 +0,0 @@ -import { useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { motion } from 'framer-motion'; -import { Database, Server, Store, Code2, Box } from 'lucide-react'; - -interface SkillItem { - name: string; - level: number; -} - -interface SkillGroup { - category: string; - items: SkillItem[]; -} - -const iconMap: { [key: string]: React.ReactNode } = { - 'Python': , - 'Server': , - 'Google Ads': , - 'Meta Ads': , - 'Shopware': , - 'Shopify': , - 'WooCommerce': , - 'JTL WAWI': , - 'AirFlow': -}; - -const Skills = () => { - const { t } = useTranslation(); - const [selectedSkill, setSelectedSkill] = useState(null); - - const skillGroups = t('pages.about.skills.skillGroups', { - returnObjects: true - }) as SkillGroup[]; - - // Alle Skills aus allen Gruppen flach auflisten - const allSkills = skillGroups.flatMap(group => group.items); - - return ( -
-
- -

- {t('pages.about.skills.title')} -

-
- {/* Skills Progress Bars */} -
- {allSkills.map((skill) => ( - setSelectedSkill(skill.name)} - onHoverEnd={() => setSelectedSkill(null)} - > -
-
- - {skill.name} - - {selectedSkill === skill.name && ( - - {t('pages.about.skills.ui.skillLevel', { - level: skill.level, - skill: skill.name - })} - - )} -
- - {skill.level}% - -
-
- -
-
- ))} -
- - {/* Skills Icons Grid */} -
- {allSkills.map((skill) => ( - setSelectedSkill(skill.name === selectedSkill ? null : skill.name)} - role="button" - aria-pressed={selectedSkill === skill.name} - aria-label={t('pages.about.skills.aria.selectSkill')} - > -
- {iconMap[skill.name] || } -
- - {skill.name} - - {selectedSkill === skill.name && ( - - {t('pages.about.skills.screenReader.skillProgress', { - skill: skill.name, - level: skill.level - })} - - )} -
- ))} -
-
-
-
-
- ); -}; - -export default Skills; \ No newline at end of file diff --git a/src/pages-vite/about/components/Workflow.tsx b/src/pages-vite/about/components/Workflow.tsx deleted file mode 100644 index 0520fed..0000000 --- a/src/pages-vite/about/components/Workflow.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import { motion } from 'framer-motion'; -import { ArrowRight } from 'lucide-react'; -import { useTranslation } from 'react-i18next'; - -interface WorkflowStep { - number: string; - title: string; -} - -const Workflow = () => { - const { t } = useTranslation(); - - const getWorkflowSteps = () => { - try { - const steps = t('pages.about.workflow.steps', { returnObjects: true }); - return Array.isArray(steps) ? steps : []; - } catch (error) { - console.error('Error loading workflow steps:', error); - return []; - } - }; - - const workflowSteps = getWorkflowSteps(); - const totalSteps = workflowSteps.length; - - const containerVariants = { - hidden: {}, - visible: { - transition: { - staggerChildren: 0.1, - }, - }, - }; - - const itemVariants = { - hidden: { opacity: 0, x: -20 }, - visible: { opacity: 1, x: 0 }, - }; - - return ( -
- {/* Section Title and Description */} -
- -
- -

- {t('pages.about.workflow.title')} -

-
-

- {t('pages.about.workflow.description')} -

-
-
- - {/* Workflow Steps */} - - {workflowSteps.map((step: WorkflowStep) => ( - -
-
- {step.number} -
-

- {step.title} -

-
- - {/* Line Indicator */} -
- ); -}; - -export default Workflow; diff --git a/src/pages-vite/about/index.tsx b/src/pages-vite/about/index.tsx deleted file mode 100644 index 3b68242..0000000 --- a/src/pages-vite/about/index.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import { useTranslation } from 'react-i18next'; -import { motion } from 'framer-motion'; -import SEO from '../../components/SEO'; -import Hero from './components/Hero'; -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(); - - const sectionVariants = { - hidden: { opacity: 0, y: 20 }, - visible: { opacity: 1, y: 0 } - }; - - return ( - <> - - - - -
- {/* Hero Section */} -
- -
- - {/* Skills Section */} - -
- -

- {t('pages.about.skills.title')} -

-

- {t('pages.about.skills.description')} -

-
- -
-
- - {/* Workflow Section */} - -
- -

- {t('pages.about.workflow.title')} -

-

- {t('pages.about.workflow.description')} -

-
- -
-
- - {/* Journey Section */} - -
- - ); -}; - -export default AboutPage; \ No newline at end of file diff --git a/src/pages-vite/blog/components/BlogPost.tsx b/src/pages-vite/blog/components/BlogPost.tsx deleted file mode 100644 index 5f3334f..0000000 --- a/src/pages-vite/blog/components/BlogPost.tsx +++ /dev/null @@ -1,519 +0,0 @@ -import React, { useEffect, useState, useCallback } from 'react'; -import { useParams, useNavigate, Link } from 'react-router-dom'; -import { motion } from 'framer-motion'; -import { Calendar, ArrowLeft, Tag, Loader2, Folder } from 'lucide-react'; -import { useTranslation } from 'react-i18next'; -import PageTransition from '../../../components/PageTransition'; -import SEO from '../../../components/SEO'; -import { trackBlogPostView } from '../../../services/gtm'; - -import { blog as blogDe } from '../../../i18n/locales/de/blog/'; -import { blog as blogEn } from '../../../i18n/locales/en/blog'; -import { blog as blogSr } from '../../../i18n/locales/sr/blog'; - -/* ======================= - Typdefinitionen -========================== */ - -// Typ für einen Blog-Post, wie er in der Komponente genutzt wird (enthält slug) -interface BlogPostType { - slug: string; - title: string; - excerpt: string; - date: string; - coverImage?: string; - category?: string; - tags?: string[]; - content: { - intro: { - title: string; - description: string; - }; - background: { - title: string; - systems: Record; - challenges: Record; - }; - tech: { - title: string; - components: { - title: string; - code: string; - }; - }; - api: { - title: string; - apparel_magic: { - title: string; - description: string; - }; - }; - conclusion: { - title: string; - requirements: Record; - results: string; - }; - }; -} - -// Typ für einen Blog-Post in der Konfiguration (ohne slug) -type BlogPostConfig = Omit; - -// Typ für die Blog-Konfiguration (UI-Text, Kategorien, Posts) -interface BlogConfig { - meta?: { - title: string; - description: string; - header: unknown; - }; - ui: { - loading: { - post: string; - }; - notFound: { - title: string; - message: string; - }; - backToBlog: string; - }; - categories: Record; - posts: Record; -} - -/* ======================= - BlogPost Component -========================== */ - -const BlogPost: React.FC = () => { - const { slug } = useParams(); - const navigate = useNavigate(); - const { i18n } = useTranslation(); - - const [post, setPost] = useState(null); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - - // Memoized Funktion zur Ermittlung der aktuellen Blog-Konfiguration anhand der Sprache. - const getBlogConfig = useCallback((): BlogConfig => { - switch (i18n.language) { - case 'en': - return blogEn as unknown as BlogConfig; - case 'sr': - return blogSr as unknown as BlogConfig; - case 'de': - default: - return blogDe as unknown as BlogConfig; - } - }, [i18n.language]); - - const blog = getBlogConfig(); - - useEffect(() => { - const loadPost = async () => { - setLoading(true); - try { - if (slug) { - const currentBlog = getBlogConfig(); - const postData = currentBlog.posts[slug]; - if (postData) { - // Wir fügen hier den slug hinzu, da er in der Konfiguration nicht enthalten ist. - setPost({ - ...postData, - slug, - }); - // Track blog post view - trackBlogPostView(postData.title, postData.category); - } else { - // Statt eine Exception zu werfen, setzen wir den Fehler direkt. - setError(new Error('Post not found')); - } - } - } catch (err) { - console.error('Error loading blog post:', err); - setError(err instanceof Error ? err : new Error('Failed to load post')); - } finally { - setLoading(false); - } - }; - loadPost(); - }, [slug, getBlogConfig]); - - const containerVariants = { - hidden: {}, - visible: { - transition: { - staggerChildren: 0.1, - }, - }, - }; - - const itemVariants = { - hidden: { opacity: 0, y: 20 }, - visible: { opacity: 1, y: 0 }, - }; - - const getFormattedDate = (date: string): string => { - const languageMap: Record = { - de: 'de-DE', - en: 'en-US', - sr: 'sr-RS', - }; - - return new Date(date).toLocaleDateString( - languageMap[i18n.language] || 'de-DE', - { - year: 'numeric', - month: 'long', - day: 'numeric', - } - ); - }; - - if (loading) { - return ( -
- - -

{blog.ui.loading.post}

-
-
- ); - } - - if (error || !post) { - return ( -
- -
-

{blog.ui.notFound.title}

-

- {error?.message || blog.ui.notFound.message} -

-
- - - {blog.ui.backToBlog} - -
-
- ); - } - - // Article Schema for SEO - const articleSchema = { - "@context": "https://schema.org", - "@type": "Article", - "headline": post.title, - "description": post.excerpt, - "datePublished": post.date, - "dateModified": post.date, - "author": { - "@type": "Person", - "name": "Damjan Savić", - "url": "https://damjan-savic.com" - }, - "publisher": { - "@type": "Person", - "name": "Damjan Savić", - "logo": { - "@type": "ImageObject", - "url": "https://damjan-savic.com/logo.svg" - } - }, - "mainEntityOfPage": { - "@type": "WebPage", - "@id": `https://damjan-savic.com/blog/${post.slug}` - }, - "articleSection": post.category ? blog.categories[post.category] : undefined, - "keywords": post.tags?.join(', ') - }; - - return ( - - - - - - - - ); -};`} - - - - -

Produktverwaltung mit Supabase

-
-
-    
-{`interface StoneProduct {
-  id: string;
-  name: string;
-  type: 'decorative_stone' | 'rustic_brick';
-  dimensions: {
-    length: 44,  // cm
-    width: 8.5,  // cm
-    thickness: 15  // mm
-  };
-  price_per_m2: number;  // 33-40 BAM
-  weight_per_m2: 32;  // kg
-  textures: string[];  // 3 verschiedene
-  available_colors: string[];
-}
-
-// Produkt-Service für Katalog
-class ProductService {
-  async getProducts(filters?: {
-    type?: string;
-    priceRange?: [number, number];
-    texture?: string;
-  }) {
-    let query = supabase
-      .from('products')
-      .select('*, product_images (*)')
-      .eq('is_active', true);
-
-    if (filters?.type) {
-      query = query.eq('type', filters.type);
-    }
-
-    const { data, error } = await query;
-    return this.transformProducts(data);
-  }
-}`}
-    
-  
-
- -

PWA für Offline-Verfügbarkeit

-
-
-    
-{`// Service Worker für Offline-Katalog
-self.addEventListener('install', event => {
-  event.waitUntil(
-    caches.open('kamenpro-v1').then(cache => {
-      return cache.addAll([
-        '/',
-        '/offline.html',
-        '/katalog',
-        // Kritische Produktbilder
-        '/images/products/dekorativni-kamen-preview.webp',
-        '/images/products/rustik-cigla-preview.webp'
-      ]);
-    })
-  );
-});
-
-// Network-first für Produktdaten
-self.addEventListener('fetch', event => {
-  if (event.request.url.includes('/api/products')) {
-    event.respondWith(
-      fetch(event.request)
-        .then(response => {
-          // Cache aktualisieren
-          const responseClone = response.clone();
-          caches.open('kamenpro-api').then(cache => {
-            cache.put(event.request, responseClone);
-          });
-          return response;
-        })
-        .catch(() => caches.match(event.request))
-    );
-  }
-});`}
-    
-  
-
- -

Lokale SEO-Erfolge

-
-

- Durch gezielte Multi-Location-Optimierung erreichten wir Top-Rankings: -

-
    -
  • Rank #1 für 'dekorativni kamen bijeljina'
  • -
  • Rank #2 für 'fasadne obloge brčko'
  • -
  • Rank #1 für 'rustik cigla tuzla'
  • -
  • Google My Business Integration für alle Standorte
  • -
  • Lokale Backlinks von Bauunternehmen
  • -
-
- -

Messbare Geschäftsergebnisse

-
-

- Die digitale Transformation brachte beeindruckende Ergebnisse: -

-
    -
  • 300% mehr Anfragen in den ersten 3 Monaten
  • -
  • Von 0 auf Platz 1-3 bei lokalen Suchanfragen
  • -
  • 45 Anfragen/Monat statt vorher ~12
  • -
  • 25-30 Projekte/Quartal statt 8-10
  • -
  • ROI von 275% in 6 Monaten
  • -
  • Expansion in neue Märkte durch Online-Präsenz
  • -
-
- -

Produktspezifikationen

-
-

- KamenPro's Produktpalette umfasst: -

-
    -
  • Dekorativer Stein: 44cm x 8.5cm, 15-20mm dick
  • -
  • Rustikale Ziegel: 5mm dick, wetterbeständig
  • -
  • Gewicht: 30-35 kg/m²
  • -
  • Material: Weißzement-Basis mit Additiven
  • -
  • Preise: 33-40 BAM (Stein), 25-30 BAM (Ziegel)
  • -
  • Varianten: 3 verschiedene Texturen, Farben nach Wunsch
  • -
-
- -

Fazit

-
-

- Die digitale Transformation von KamenPro zeigt eindrucksvoll, wie ein traditioneller Handwerksbetrieb durch moderne Webtechnologie neue Märkte erschließen kann. - Die Kombination aus Multi-Location SEO, optimierter Produktpräsentation und technischer Exzellenz führte zu einer Verdreifachung der Kundenanfragen. - Besonders bemerkenswert: Ein lokaler Steinverkleidungs-Hersteller aus Bijeljina erreicht nun Kunden in der gesamten Region - - ein Beweis dafür, dass durchdachte Digitalisierung auch im traditionellen Handwerk messbare Erfolge bringt. -

-
\ No newline at end of file diff --git a/src/pages-vite/portfolio/projects/power-platform-governance.mdx b/src/pages-vite/portfolio/projects/power-platform-governance.mdx deleted file mode 100644 index 23c7321..0000000 --- a/src/pages-vite/portfolio/projects/power-platform-governance.mdx +++ /dev/null @@ -1,320 +0,0 @@ ---- -slug: "power-platform-governance" -title: "Power Platform Governance & Automation Suite" -description: "Enterprise-Governance-Plattform für Microsoft Power Platform und SharePoint Online" -excerpt: "Zentrale Verwaltung, Überwachung und Automatisierung der gesamten M365-Umgebung - von Tenant-Provisionierung bis Compliance-Monitoring." -date: "2024-09-15" -category: "Enterprise Software" -coverImage: "/images/projects/power-platform-governance/cover.jpg" -client: "Enterprise CoE Teams" -duration: "6 Monate" -url: "https://governance-demo.azurewebsites.net" -repository: "" -documentation: "/docs/power-platform-governance" -published: true -featured: true -technologies: ["React", "TypeScript", "Node.js", "Microsoft Graph", "PowerShell", "Azure Functions", "GraphQL", "Fluent UI"] -tags: ["Microsoft 365", "Power Platform", "SharePoint", "Governance", "Automation", "Enterprise"] ---- - -
-

- Eine umfassende Enterprise-Governance-Plattform, die IT-Administratoren und Power Platform CoE Teams ermöglicht, ihre gesamte M365-Umgebung zentral zu verwalten. Von automatisierter Provisionierung über Compliance-Monitoring bis zur intelligenten Ressourcenverwaltung - alles in einer einzigen, leistungsstarken Lösung. -

-
- -
- -

Die Herausforderung

-
-

- Unkontrolliertes Wachstum von Power Apps, Flows und SharePoint-Sites führte zu kritischen Problemen: -

-
    -
  • Shadow IT durch ungovernte Citizen Development
  • -
  • Compliance-Risiken und Datenschutzverletzungen
  • -
  • Explodierende Lizenzkosten durch ungenutzte Ressourcen
  • -
  • Fehlende Übersicht über die Power Platform Landschaft
  • -
  • Manuelle, fehleranfällige Provisionierungsprozesse
  • -
  • Inkonsistente Governance-Policies über Teams hinweg
  • -
-
- -

Die Lösung

-
-

- Eine zentrale Governance-Plattform mit folgenden Kernbausteinen: -

-
    -
  • Automated Provisioning Hub für Sites, Teams und Environments
  • -
  • Real-Time Monitoring Dashboard mit Live Activity Streams
  • -
  • Power Platform Governance Center mit DLP Policy Enforcement
  • -
  • Compliance & Security Suite mit Permission Analyzer
  • -
  • PowerShell Automation Framework für Custom Operations
  • -
  • Intelligent Resource Optimization mit ML-based Predictions
  • -
-
- -

Technische Architektur

-
-
-    
-{`┌─────────────────┐     ┌──────────────────┐     ┌─────────────────┐
-│   Web Portal    │────▶│  GraphQL API     │────▶│  Auth Service   │
-└─────────────────┘     └──────────────────┘     └─────────────────┘
-                                 │
-                    ┌────────────┴────────────┐
-                    ▼                         ▼
-          ┌─────────────────┐       ┌─────────────────┐
-          │ Provisioning    │       │ Monitoring      │
-          │ Service         │       │ Service         │
-          └─────────────────┘       └─────────────────┘
-                   │                         │
-                   └────────────┬────────────┘
-                               ▼
-                   ┌─────────────────┐
-                   │ Message Queue   │
-                   │ (Service Bus)   │
-                   └─────────────────┘
-                            │
-        ┌───────────────────┼───────────────────┐
-        ▼                   ▼                   ▼
-┌───────────────┐  ┌───────────────┐  ┌───────────────┐
-│ PowerShell    │  │ Graph API     │  │ Analytics     │
-│ Executor      │  │ Gateway       │  │ Engine        │
-└───────────────┘  └───────────────┘  └───────────────┘`}
-    
-  
-
- -

Automated Provisioning Hub

-
-

Site Provisioning Engine

-
-    
-{`class SiteProvisioningEngine {
-  async provisionSite(template: SiteTemplate, params: ProvisioningParams) {
-    // Validate naming conventions
-    const siteName = this.validateNaming(params.name);
-    
-    // Create site from template
-    const site = await this.graph.sites.create({
-      displayName: siteName,
-      template: template.id,
-      owner: params.owner,
-      classification: params.classification
-    });
-    
-    // Apply custom configurations
-    await this.applyCustomizations(site, template.customizations);
-    
-    // Setup permissions
-    await this.configurePermissions(site, params.permissions);
-    
-    // Execute post-provisioning workflows
-    await this.runPostProvisioningWorkflows(site, template.workflows);
-    
-    return site;
-  }
-}`}
-    
-  
-
- -

Real-Time Monitoring

-
-
-    
-{`const ActivityMonitor: React.FC = () => {
-  const { data: activities } = useSubscription(ACTIVITY_SUBSCRIPTION);
-  
-  return (
-    
-      Platform Activity
-      
-    
-  );
-};`}
-    
-  
-
- -

PowerShell Automation Framework

-
-
-    
-{`function New-GovernanceReport {
-    [CmdletBinding()]
-    param (
-        [Parameter(Mandatory)]
-        [string]$TenantId,
-        
-        [ValidateSet('Sites','Apps','Flows','All')]
-        [string]$Scope = 'All',
-        
-        [DateTime]$StartDate = (Get-Date).AddDays(-30)
-    )
-    
-    # Complex governance analysis logic
-    $results = @{
-        ComplianceScore = Get-ComplianceScore -TenantId $TenantId
-        UnusedResources = Find-UnusedResources -Scope $Scope
-        SecurityRisks = Analyze-SecurityPosture -StartDate $StartDate
-        CostOptimization = Calculate-OptimizationPotential
-    }
-    
-    Export-GovernanceReport -Results $results
-}`}
-    
-  
-
- -

Compliance & Security Suite

-
-

Permission Analyzer

-
    -
  • Cross-tenant Permission Reports mit visueller Darstellung
  • -
  • Overprivileged Users Detection durch ML-Algorithmen
  • -
  • External Sharing Audit mit Risikobewertung
  • -
  • Inheritance Breaking Analysis für SharePoint Sites
  • -
  • Automated Access Reviews mit Approval Workflows
  • -
- -

Data Loss Prevention

-
    -
  • Custom DLP Policy Builder mit Drag-and-Drop Interface
  • -
  • Sensitive Data Discovery durch Pattern Matching
  • -
  • Real-time Policy Violation Alerts
  • -
  • Automated Remediation Actions
  • -
  • Compliance Dashboards mit Trend Analysis
  • -
-
- -

Policy as Code

-
-
-    
-{`# Governance Policy Definition
-apiVersion: governance/v1
-kind: SitePolicy
-metadata:
-  name: external-sharing-policy
-spec:
-  rules:
-    - effect: Deny
-      resource: "sites/*"
-      action: "share.external"
-      condition:
-        classification: "Confidential"
-    - effect: Audit
-      resource: "sites/*"
-      action: "permissions.break"
-  enforcement:
-    mode: "preventive"
-    notifications:
-      - channel: "teams"
-        webhook: "${TEAMS_WEBHOOK_URL}"`}
-    
-  
-
- -

Intelligent Resource Optimization

-
-
-    
-{`class ResourceOptimizer {
-  async analyzeUsage() {
-    const resources = await this.getAllResources();
-    
-    for (const resource of resources) {
-      const usage = await this.mlEngine.predictUsage(resource);
-      
-      if (usage.probability < 0.1) {
-        await this.scheduleForCleanup(resource, {
-          reason: 'Low usage probability',
-          confidence: usage.confidence,
-          suggestedAction: 'archive',
-          scheduledDate: this.calculateCleanupDate(usage)
-        });
-      }
-    }
-    
-    return this.generateOptimizationReport();
-  }
-}`}
-    
-  
-
- -

Projekt-Metriken & Ergebnisse

-
-

Quantitative Ergebnisse

-
    -
  • 95% schneller: Provisioning Zeit von 5 Min auf 15 Sek reduziert
  • -
  • 94% Compliance: Rate von 67% gesteigert
  • -
  • 40% Kostenreduktion: Durch Bereinigung ungenutzter Ressourcen
  • -
  • 3x Produktivität: Admin-Effizienz verdreifacht
  • -
  • 80% schneller: Incident Response Time verbessert
  • -
- -

Technische Achievements

-
    -
  • 35.000+ Lines of Code
  • -
  • 45+ React Components mit Fluent UI
  • -
  • 120+ Custom PowerShell Cmdlets
  • -
  • 80+ GraphQL Endpoints
  • -
  • 92% Test Coverage
  • -
  • 25+ aktive Enterprise-Tenants
  • -
  • 50.000+ verwaltete Ressourcen
  • -
-
- -

UI/UX Features

-
-
    -
  • Customizable Dashboards mit Drag-and-Drop Widgets
  • -
  • Dark Mode mit vollem Theme Support
  • -
  • Command Palette für Quick Actions (Ctrl+K)
  • -
  • Bulk Operations mit Multi-Select
  • -
  • Export nach Excel, PDF und Power BI
  • -
  • Mobile Responsive für Tablet-Nutzung
  • -
-
- -

Zukunftsperspektiven

-
-

Roadmap 2025

-
    -
  • AI-Powered Insights mit Anomalie-Erkennung
  • -
  • Copilot Integration für Natural Language Governance
  • -
  • Microsoft Fabric Analytics Integration
  • -
  • Container Apps für serverlose PowerShell-Execution
  • -
  • Zero Trust Security Model Implementation
  • -
- -

Vision 2026

-
    -
  • Autonomous Governance mit selbstheilender Umgebung
  • -
  • Cross-Cloud Support für AWS/GCP
  • -
  • Blockchain-basierte Audit Logs
  • -
  • Quantum-Safe Encryption
  • -
-
- -

Fazit

-
-

- Diese Plattform setzt neue Standards für Power Platform Governance und demonstriert, wie moderne Cloud-Technologien mit Microsoft 365 kombiniert werden können, um Enterprise-Grade Automatisierung und Compliance zu erreichen. - Die Lösung ermöglicht es Unternehmen, die Balance zwischen Innovation und Kontrolle zu finden, während sie gleichzeitig Kosten senkt und die Sicherheit erhöht. -

-
\ No newline at end of file diff --git a/src/pages-vite/portfolio/projects/smart-warehouse.mdx b/src/pages-vite/portfolio/projects/smart-warehouse.mdx deleted file mode 100644 index 7545e3d..0000000 --- a/src/pages-vite/portfolio/projects/smart-warehouse.mdx +++ /dev/null @@ -1,270 +0,0 @@ ---- -slug: "smart-warehouse" -title: "Intelligente Lagerverwaltung mit Computer Vision" -description: "KI-gestütztes System zur Echtzeit-Bestandsführung und Optimierung von Lagerprozessen" -excerpt: "Entwicklung eines autonomen Lagerverwaltungssystems mit Computer Vision, IoT-Sensorik und Machine Learning für präzise Bestandsführung." -date: "2024-06-15" -category: "AI & Automation" -coverImage: "/images/projects/smart-warehouse/cover.jpg" -client: "LogiTech Solutions GmbH" -duration: "4 Monate" -url: "https://warehouse-demo.example.com" -repository: "" -documentation: "/case-studies/smart-warehouse" -published: true -featured: true -technologies: ["YOLOv8", "Python", "FastAPI", "React", "PostgreSQL", "Docker", "Kubernetes", "IoT", "MQTT"] -tags: ["Computer Vision", "Machine Learning", "IoT", "Edge Computing", "Automation"] ---- - -
-

- Ein mittelständischer Logistikdienstleister transformierte seine manuelle Lagerverwaltung durch ein vollautomatisiertes System, das Computer Vision, IoT-Sensorik und Machine Learning kombiniert. Diese Case Study zeigt, wie KI-gestützte Bilderkennung zu 98,7% Genauigkeit bei der Bestandserfassung führte. -

-
- -
- -

Die Herausforderung

-
-

- Die manuelle Bestandsführung führte zu erheblichen operativen Problemen: -

-
    -
  • Diskrepanzen zwischen tatsächlichem und gebuchtem Bestand von bis zu 15%
  • -
  • Zeitintensive manuelle Inventuren (3-4 Tage pro Quartal)
  • -
  • Fehlende Echtzeit-Transparenz über Lagerbestände
  • -
  • Ineffiziente Lagerplatznutzung durch fehlende Optimierung
  • -
  • Hohe Personalkosten durch manuelle Prozesse
  • -
-
- -

Die Lösung

-
-

- Entwicklung eines vollautomatisierten Lagerverwaltungssystems mit folgenden Kernkomponenten: -

-
    -
  • KI-gestützte Objekterkennung mittels hochauflösender Kameras
  • -
  • IoT-Gewichtssensoren zur Validierung
  • -
  • Predictive Analytics für Bestandsoptimierung
  • -
  • Echtzeit-Dashboard mit mobilem Zugriff
  • -
  • Automatische Nachbestellungstrigger
  • -
-
- -

Systemarchitektur

-
-
-    
-{`┌─────────────┐     ┌──────────────┐     ┌────────────┐
-│   Kameras   │────▶│ Edge Server  │────▶│  ML Cloud  │
-└─────────────┘     └──────────────┘     └────────────┘
-       │                    │                    │
-       ▼                    ▼                    ▼
-┌─────────────┐     ┌──────────────┐     ┌────────────┐
-│ IoT Sensoren│────▶│ MQTT Broker  │────▶│   API GW   │
-└─────────────┘     └──────────────┘     └────────────┘
-                            │                    │
-                            ▼                    ▼
-                    ┌──────────────┐     ┌────────────┐
-                    │  PostgreSQL  │◀────│ Dashboard  │
-                    └──────────────┘     └────────────┘`}
-    
-  
-
- -

Computer Vision Pipeline

-
-
-    
-{`# Objekterkennung mit YOLOv8
-model = YOLO('yolov8x-custom.pt')
-results = model.predict(
-    source=camera_feed,
-    conf=0.85,
-    save=False,
-    stream=True
-)
-
-# Bestandszählung und Klassifizierung
-inventory_count = process_detections(results)
-validate_with_sensors(inventory_count, weight_data)
-
-# Echtzeit-Update der Datenbank
-async def update_inventory(items: List[DetectedItem]):
-    async with db.transaction():
-        for item in items:
-            await db.execute(
-                """UPDATE inventory 
-                   SET quantity = $1, 
-                       last_seen = $2,
-                       confidence = $3
-                   WHERE sku = $4""",
-                item.quantity, 
-                datetime.now(),
-                item.confidence,
-                item.sku
-            )`}
-    
-  
-
- -

IoT-Integration

-
-
-    
-{`class IoTSensorManager:
-    def __init__(self):
-        self.mqtt_client = mqtt.Client()
-        self.sensors = {}
-        
-    async def process_weight_data(self, sensor_id: str, weight: float):
-        """Validiert CV-Ergebnisse mit Gewichtsdaten"""
-        location = self.sensors[sensor_id].location
-        expected_items = await self.get_cv_prediction(location)
-        
-        # Gewichtsvalidierung
-        expected_weight = sum(item.weight for item in expected_items)
-        variance = abs(weight - expected_weight) / expected_weight
-        
-        if variance > 0.1:  # 10% Toleranz
-            await self.trigger_recount(location)
-            
-        return {
-            'sensor_id': sensor_id,
-            'measured_weight': weight,
-            'expected_weight': expected_weight,
-            'variance': variance,
-            'status': 'valid' if variance <= 0.1 else 'recount_needed'
-        }`}
-    
-  
-
- -

Implementierungsphasen

-
-

Phase 1: Proof of Concept (4 Wochen)

-
    -
  • Entwicklung eines Prototyps für einen Lagerbereich
  • -
  • Training des ML-Modells mit 10.000+ annotierten Bildern
  • -
  • Integration von 5 Testkameras und 20 IoT-Sensoren
  • -
  • Validierung der Erkennungsgenauigkeit
  • -
- -

Phase 2: Skalierung (8 Wochen)

-
    -
  • Rollout auf gesamtes Lager (5.000m²)
  • -
  • Installation von 45 Kameras und 200+ Sensoren
  • -
  • Entwicklung des Echtzeit-Dashboards
  • -
  • Integration in bestehendes ERP-System
  • -
- -

Phase 3: Optimierung (4 Wochen)

-
    -
  • Fine-Tuning der ML-Modelle
  • -
  • Implementierung von Predictive Analytics
  • -
  • Mobile App Entwicklung
  • -
  • Schulung der Mitarbeiter
  • -
-
- -

Herausforderungen & Lösungen

-
-

Variierende Lichtverhältnisse

-

- Problem: Schatten und Reflexionen beeinträchtigten die Erkennung
- Lösung: HDR-Kameras + adaptive Bildvorverarbeitung + Augmentierung der Trainingsdaten -

- -

Echtzeitverarbeitung großer Datenmengen

-

- Problem: 45 Kameras generierten 2TB Daten/Tag
- Lösung: Edge Computing für Vorverarbeitung + Stream Processing mit Apache Kafka -

- -

Integration in Legacy-Systeme

-

- Problem: 20 Jahre altes ERP ohne moderne APIs
- Lösung: Entwicklung eines Adapter-Layers mit bidirektionaler Synchronisation -

-
- -

Performance Metriken

-
-
-    
-{`Performance:
-  - Bildverarbeitung: <50ms pro Frame
-  - API Response Time: p95 < 100ms
-  - System Uptime: 99.95%
-  - Datenverarbeitung: 500 Events/Sekunde
-
-Skalierung:
-  - Kameras: 45 aktiv, bis 200 möglich
-  - IoT Sensoren: 200+
-  - Concurrent Users: 50+
-  - Datenspeicher: 50TB (3 Jahre Historie)
-
-ML Performance:
-  - mAP@50: 0.92
-  - Inference Time: 23ms
-  - False Positive Rate: <2%
-  - Model Size: 138MB`}
-    
-  
-
- -

Ergebnisse und Auswirkungen

-
-

Quantitative Ergebnisse

-
    -
  • 98,7% Genauigkeit bei der Bestandserfassung
  • -
  • 85% Reduktion der Inventurzeit
  • -
  • 60% weniger Fehlbestände
  • -
  • ROI nach 14 Monaten erreicht
  • -
  • 35% Steigerung der Lagerplatznutzung
  • -
- -

Qualitative Verbesserungen

-
    -
  • Echtzeit-Transparenz über alle Lagerbestände
  • -
  • Proaktive Nachbestellung verhindert Lieferengpässe
  • -
  • Mitarbeiter fokussieren sich auf wertschöpfende Tätigkeiten
  • -
  • Deutlich reduzierte Fehlerquote
  • -
  • Verbesserte Kundenzufriedenheit durch höhere Liefertreue
  • -
-
- -

Lessons Learned

-
-
    -
  1. Edge Computing ist essentiell: Die Vorverarbeitung direkt an der Kamera reduzierte die Netzwerklast um 70%
  2. -
  3. Datenqualität vor Quantität: 1.000 hochqualitative Annotationen waren wertvoller als 10.000 automatisch generierte
  4. -
  5. Iterative Entwicklung: Frühe Pilotierung in einem Bereich ermöglichte schnelle Anpassungen
  6. -
  7. Change Management: Frühzeitige Einbindung der Mitarbeiter war entscheidend für die Akzeptanz
  8. -
-
- -

Zukunftsperspektiven

-
-

- Nächste Entwicklungsstufen: -

-
    -
  • Integration von Robotik für automatisierte Kommissionierung
  • -
  • Erweiterung auf Außenlager und mobile Einheiten
  • -
  • KI-basierte Vorhersage von Wartungsbedarf
  • -
  • Blockchain-Integration für Supply Chain Transparenz
  • -
  • AR-Brillen für Lagermitarbeiter mit visuellen Hinweisen
  • -
-
- -

Fazit

-
-

- Dieses Projekt demonstriert die erfolgreiche Transformation traditioneller Lagerprozesse durch modernste Computer Vision und IoT-Technologien. - Die Kombination aus technischer Innovation und praxisorientierter Umsetzung schafft messbaren Mehrwert und ebnet den Weg für die Logistik 4.0. - Das System wurde als White-Label-Lösung konzipiert und kann mit minimalen Anpassungen in anderen Lagern eingesetzt werden. -

-
\ No newline at end of file diff --git a/src/pages-vite/portfolio/projects/website-mit-ki.mdx b/src/pages-vite/portfolio/projects/website-mit-ki.mdx deleted file mode 100644 index 27e3ad0..0000000 --- a/src/pages-vite/portfolio/projects/website-mit-ki.mdx +++ /dev/null @@ -1,168 +0,0 @@ ---- -slug: "website-mit-ki" -title: "Eigene Website mit KI bauen: Von Null zur fertigen Seite" -description: "Schritt-für-Schritt Anleitung zum Aufbau einer professionellen Website mit Claude Code - DSGVO-konform, mehrsprachig, mit Portfolio und Blog" -excerpt: "Wie du in unter einer Stunde eine komplette Website erstellst - ohne eine einzige Zeile Code selbst zu schreiben." -date: "2024-12-13" -category: "KI & Automatisierung" -coverImage: "/images/projects/website-mit-ki/cover.jpg" -client: "Eigenprojekt" -duration: "30 Minuten" -url: "" -repository: "" -documentation: "" -published: true -featured: true -technologies: ["Claude Code", "React", "TypeScript", "Vercel", "GitHub", "Vite"] -tags: ["KI", "Website", "No-Code", "Tutorial", "Claude Code", "Vercel"] -videoUrl: "https://www.tella.tv/video/baue-deine-website-noch-heute-mit-ki-8f7v" ---- - -
-