From 009eb1d99f1de3b5071345633c5a336cfd478b44 Mon Sep 17 00:00:00 2001 From: Damjan Savic Date: Sun, 25 Jan 2026 06:23:08 +0100 Subject: [PATCH 1/8] auto-claude: subtask-1-1 - Verify no imports from components-vite or pages-vite Co-Authored-By: Claude Sonnet 4.5 --- .auto-claude-security.json | 227 +++++++++++++++++++++++++++++++++++++ .auto-claude-status | 25 ++++ .claude_settings.json | 39 +++++++ .gitignore | 5 +- 4 files changed, 295 insertions(+), 1 deletion(-) create mode 100644 .auto-claude-security.json create mode 100644 .auto-claude-status create mode 100644 .claude_settings.json diff --git a/.auto-claude-security.json b/.auto-claude-security.json new file mode 100644 index 0000000..eed60a2 --- /dev/null +++ b/.auto-claude-security.json @@ -0,0 +1,227 @@ +{ + "base_commands": [ + ".", + "[", + "[[", + "ag", + "awk", + "basename", + "bash", + "bc", + "break", + "cat", + "cd", + "chmod", + "clear", + "cmp", + "column", + "comm", + "command", + "continue", + "cp", + "curl", + "cut", + "date", + "df", + "diff", + "dig", + "dirname", + "du", + "echo", + "egrep", + "env", + "eval", + "exec", + "exit", + "expand", + "export", + "expr", + "false", + "fd", + "fgrep", + "file", + "find", + "fmt", + "fold", + "gawk", + "gh", + "git", + "grep", + "gunzip", + "gzip", + "head", + "help", + "host", + "iconv", + "id", + "jobs", + "join", + "jq", + "kill", + "killall", + "less", + "let", + "ln", + "ls", + "lsof", + "man", + "mkdir", + "mktemp", + "more", + "mv", + "nl", + "paste", + "pgrep", + "ping", + "pkill", + "popd", + "printenv", + "printf", + "ps", + "pushd", + "pwd", + "read", + "readlink", + "realpath", + "reset", + "return", + "rev", + "rg", + "rm", + "rmdir", + "sed", + "seq", + "set", + "sh", + "shuf", + "sleep", + "sort", + "source", + "split", + "stat", + "tail", + "tar", + "tee", + "test", + "time", + "timeout", + "touch", + "tr", + "tree", + "true", + "type", + "uname", + "unexpand", + "uniq", + "unset", + "unzip", + "watch", + "wc", + "wget", + "whereis", + "which", + "whoami", + "xargs", + "yes", + "yq", + "zip", + "zsh" + ], + "stack_commands": [ + "ar", + "clang", + "clang++", + "cmake", + "composer", + "dive", + "docker", + "docker-buildx", + "docker-compose", + "dockerfile", + "eslint", + "g++", + "gcc", + "ipython", + "jupyter", + "ld", + "make", + "meson", + "next", + "ninja", + "nm", + "node", + "notebook", + "npm", + "npx", + "objdump", + "pdb", + "php", + "pip", + "pip3", + "pipx", + "pnpm", + "pnpx", + "pudb", + "python", + "python3", + "react-scripts", + "strip", + "ts-node", + "tsc", + "tsx", + "vitest" + ], + "script_commands": [ + "bun", + "npm", + "pnpm", + "yarn" + ], + "custom_commands": [], + "detected_stack": { + "languages": [ + "python", + "javascript", + "typescript", + "php", + "c" + ], + "package_managers": [ + "pnpm" + ], + "frameworks": [ + "nextjs", + "react", + "vitest", + "eslint" + ], + "databases": [], + "infrastructure": [ + "docker" + ], + "cloud_providers": [], + "code_quality_tools": [], + "version_managers": [] + }, + "custom_scripts": { + "npm_scripts": [ + "dev", + "build", + "start", + "lint", + "build:images", + "generate:images", + "generate:images:dry", + "test", + "test:coverage" + ], + "make_targets": [], + "poetry_scripts": [], + "cargo_aliases": [], + "shell_scripts": [] + }, + "project_dir": "C:\\Users\\damja\\WebstormProjects\\Portfolio", + "created_at": "2026-01-22T15:28:38.237190", + "project_hash": "c4ad399e16be367eb4e6b076fe1d9ee3", + "inherited_from": "C:\\Users\\damja\\WebstormProjects\\Portfolio" +} \ No newline at end of file diff --git a/.auto-claude-status b/.auto-claude-status new file mode 100644 index 0000000..d0a1f49 --- /dev/null +++ b/.auto-claude-status @@ -0,0 +1,25 @@ +{ + "active": true, + "spec": "029-remove-dead-code-components-vite-and-pages-vite-fo", + "state": "planning", + "subtasks": { + "completed": 0, + "total": 0, + "in_progress": 1, + "failed": 0 + }, + "phase": { + "current": "Pre-Deletion Safety Verification", + "id": null, + "total": 2 + }, + "workers": { + "active": 0, + "max": 1 + }, + "session": { + "number": 2, + "started_at": "2026-01-25T06:18:19.396966" + }, + "last_update": "2026-01-25T06:22:03.401129" +} \ No newline at end of file diff --git a/.claude_settings.json b/.claude_settings.json new file mode 100644 index 0000000..147ea0f --- /dev/null +++ b/.claude_settings.json @@ -0,0 +1,39 @@ +{ + "sandbox": { + "enabled": true, + "autoAllowBashIfSandboxed": true + }, + "permissions": { + "defaultMode": "acceptEdits", + "allow": [ + "Read(./**)", + "Write(./**)", + "Edit(./**)", + "Glob(./**)", + "Grep(./**)", + "Read(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude\\worktrees\\tasks\\029-remove-dead-code-components-vite-and-pages-vite-fo/**)", + "Write(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude\\worktrees\\tasks\\029-remove-dead-code-components-vite-and-pages-vite-fo/**)", + "Edit(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude\\worktrees\\tasks\\029-remove-dead-code-components-vite-and-pages-vite-fo/**)", + "Glob(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude\\worktrees\\tasks\\029-remove-dead-code-components-vite-and-pages-vite-fo/**)", + "Grep(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude\\worktrees\\tasks\\029-remove-dead-code-components-vite-and-pages-vite-fo/**)", + "Read(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude\\worktrees\\tasks\\029-remove-dead-code-components-vite-and-pages-vite-fo\\.auto-claude\\specs\\029-remove-dead-code-components-vite-and-pages-vite-fo/**)", + "Write(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude\\worktrees\\tasks\\029-remove-dead-code-components-vite-and-pages-vite-fo\\.auto-claude\\specs\\029-remove-dead-code-components-vite-and-pages-vite-fo/**)", + "Edit(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude\\worktrees\\tasks\\029-remove-dead-code-components-vite-and-pages-vite-fo\\.auto-claude\\specs\\029-remove-dead-code-components-vite-and-pages-vite-fo/**)", + "Read(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude/**)", + "Write(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude/**)", + "Edit(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude/**)", + "Glob(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude/**)", + "Grep(C:\\Users\\damja\\WebstormProjects\\Portfolio\\.auto-claude/**)", + "Bash(*)", + "WebFetch(*)", + "WebSearch(*)", + "mcp__context7__resolve-library-id(*)", + "mcp__context7__get-library-docs(*)", + "mcp__graphiti-memory__search_nodes(*)", + "mcp__graphiti-memory__search_facts(*)", + "mcp__graphiti-memory__add_episode(*)", + "mcp__graphiti-memory__get_episodes(*)", + "mcp__graphiti-memory__get_entity_edge(*)" + ] + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index a484ab2..fa65296 100644 --- a/.gitignore +++ b/.gitignore @@ -81,4 +81,7 @@ supabase/.temp/ .history/ # Source images (originals before optimization) -source-images/ \ No newline at end of file +source-images/ + +# Auto Claude data directory +.auto-claude/ From 26955a7b6459fdc3bcff65646426d8a9cec13e79 Mon Sep 17 00:00:00 2001 From: Damjan Savic Date: Sun, 25 Jan 2026 06:27:10 +0100 Subject: [PATCH 2/8] auto-claude: subtask-1-2 - Verify current build succeeds before deletion Verified that Next.js build completes successfully before proceeding with deletion of components-vite and pages-vite directories. Build exited with code 0 (success). Co-Authored-By: Claude Sonnet 4.5 --- .auto-claude-status | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.auto-claude-status b/.auto-claude-status index d0a1f49..6cb6ef2 100644 --- a/.auto-claude-status +++ b/.auto-claude-status @@ -1,10 +1,10 @@ { "active": true, "spec": "029-remove-dead-code-components-vite-and-pages-vite-fo", - "state": "planning", + "state": "building", "subtasks": { - "completed": 0, - "total": 0, + "completed": 1, + "total": 8, "in_progress": 1, "failed": 0 }, @@ -18,8 +18,8 @@ "max": 1 }, "session": { - "number": 2, + "number": 3, "started_at": "2026-01-25T06:18:19.396966" }, - "last_update": "2026-01-25T06:22:03.401129" + "last_update": "2026-01-25T06:23:54.704974" } \ No newline at end of file From 3b4a10c90e5c53f05388164c5436f72bda870e9d Mon Sep 17 00:00:00 2001 From: Damjan Savic Date: Sun, 25 Jan 2026 06:28:08 +0100 Subject: [PATCH 3/8] auto-claude: subtask-2-1 - Delete src/components-vite directory (299KB, 50 fi --- .auto-claude-status | 8 +- src/components-vite/Alert.tsx | 43 -- src/components-vite/Badge.tsx | 41 -- src/components-vite/Breadcrumbs.tsx | 77 --- src/components-vite/Button.tsx | 60 --- src/components-vite/Card.tsx | 84 --- src/components-vite/ContactBar.tsx | 36 -- src/components-vite/ContactForm.tsx | 125 ----- src/components-vite/ContactInfo.tsx | 40 -- src/components-vite/CookieBanner.tsx | 490 ------------------ src/components-vite/DebugOverlay.tsx | 41 -- src/components-vite/ErrorBoundary.tsx | 61 --- src/components-vite/FAQSection.tsx | 99 ---- src/components-vite/FloatingPaths.tsx | 46 -- src/components-vite/Footer.tsx | 129 ----- src/components-vite/GlobalBackground.tsx | 159 ------ src/components-vite/HreflangTags.tsx | 66 --- src/components-vite/ImageWithFallback.tsx | 61 --- src/components-vite/Input.tsx | 25 - src/components-vite/Label.tsx | 15 - src/components-vite/LanguageSwitcher.tsx | 98 ---- src/components-vite/Layout.tsx | 274 ---------- src/components-vite/LazyComponent.tsx | 17 - src/components-vite/LoadingSpinner.tsx | 102 ---- src/components-vite/MDXProvider.tsx | 28 - src/components-vite/NavLink.tsx | 32 -- src/components-vite/PageContainer.tsx | 20 - src/components-vite/PageTransition.tsx | 25 - src/components-vite/PerformanceMonitor.tsx | 199 ------- src/components-vite/PostNavigation.tsx | 55 -- .../ProjectContentTemplate.tsx | 171 ------ src/components-vite/ProjectShowcase.tsx | 161 ------ src/components-vite/ResponsiveImage.tsx | 44 -- src/components-vite/RouteErrorBoundary.tsx | 52 -- src/components-vite/SEO.tsx | 183 ------- src/components-vite/ScrollAnimation.tsx | 44 -- src/components-vite/ScrollContext.tsx | 56 -- src/components-vite/ShareMenu.tsx | 104 ---- src/components-vite/SkeletonLoader.tsx | 52 -- src/components-vite/SkillsMatrix.tsx | 60 --- src/components-vite/SkipToContent.tsx | 23 - src/components-vite/TestimonialCard.tsx | 51 -- src/components-vite/TextArea.tsx | 29 -- .../schemas/BreadcrumbSchema.tsx | 102 ---- src/components-vite/schemas/HowToSchema.tsx | 160 ------ .../schemas/LocalBusinessSchema.tsx | 158 ------ .../schemas/LocalizedSchemas.tsx | 256 --------- src/components-vite/schemas/PersonSchema.tsx | 175 ------- src/components-vite/schemas/ProjectSchema.tsx | 58 --- src/components-vite/schemas/ServiceSchema.tsx | 138 ----- src/components-vite/schemas/WebsiteSchema.tsx | 136 ----- 51 files changed, 4 insertions(+), 4765 deletions(-) delete mode 100644 src/components-vite/Alert.tsx delete mode 100644 src/components-vite/Badge.tsx delete mode 100644 src/components-vite/Breadcrumbs.tsx delete mode 100644 src/components-vite/Button.tsx delete mode 100644 src/components-vite/Card.tsx delete mode 100644 src/components-vite/ContactBar.tsx delete mode 100644 src/components-vite/ContactForm.tsx delete mode 100644 src/components-vite/ContactInfo.tsx delete mode 100644 src/components-vite/CookieBanner.tsx delete mode 100644 src/components-vite/DebugOverlay.tsx delete mode 100644 src/components-vite/ErrorBoundary.tsx delete mode 100644 src/components-vite/FAQSection.tsx delete mode 100644 src/components-vite/FloatingPaths.tsx delete mode 100644 src/components-vite/Footer.tsx delete mode 100644 src/components-vite/GlobalBackground.tsx delete mode 100644 src/components-vite/HreflangTags.tsx delete mode 100644 src/components-vite/ImageWithFallback.tsx delete mode 100644 src/components-vite/Input.tsx delete mode 100644 src/components-vite/Label.tsx delete mode 100644 src/components-vite/LanguageSwitcher.tsx delete mode 100644 src/components-vite/Layout.tsx delete mode 100644 src/components-vite/LazyComponent.tsx delete mode 100644 src/components-vite/LoadingSpinner.tsx delete mode 100644 src/components-vite/MDXProvider.tsx delete mode 100644 src/components-vite/NavLink.tsx delete mode 100644 src/components-vite/PageContainer.tsx delete mode 100644 src/components-vite/PageTransition.tsx delete mode 100644 src/components-vite/PerformanceMonitor.tsx delete mode 100644 src/components-vite/PostNavigation.tsx delete mode 100644 src/components-vite/ProjectContentTemplate.tsx delete mode 100644 src/components-vite/ProjectShowcase.tsx delete mode 100644 src/components-vite/ResponsiveImage.tsx delete mode 100644 src/components-vite/RouteErrorBoundary.tsx delete mode 100644 src/components-vite/SEO.tsx delete mode 100644 src/components-vite/ScrollAnimation.tsx delete mode 100644 src/components-vite/ScrollContext.tsx delete mode 100644 src/components-vite/ShareMenu.tsx delete mode 100644 src/components-vite/SkeletonLoader.tsx delete mode 100644 src/components-vite/SkillsMatrix.tsx delete mode 100644 src/components-vite/SkipToContent.tsx delete mode 100644 src/components-vite/TestimonialCard.tsx delete mode 100644 src/components-vite/TextArea.tsx delete mode 100644 src/components-vite/schemas/BreadcrumbSchema.tsx delete mode 100644 src/components-vite/schemas/HowToSchema.tsx delete mode 100644 src/components-vite/schemas/LocalBusinessSchema.tsx delete mode 100644 src/components-vite/schemas/LocalizedSchemas.tsx delete mode 100644 src/components-vite/schemas/PersonSchema.tsx delete mode 100644 src/components-vite/schemas/ProjectSchema.tsx delete mode 100644 src/components-vite/schemas/ServiceSchema.tsx delete mode 100644 src/components-vite/schemas/WebsiteSchema.tsx diff --git a/.auto-claude-status b/.auto-claude-status index 6cb6ef2..73299f4 100644 --- a/.auto-claude-status +++ b/.auto-claude-status @@ -3,13 +3,13 @@ "spec": "029-remove-dead-code-components-vite-and-pages-vite-fo", "state": "building", "subtasks": { - "completed": 1, + "completed": 2, "total": 8, "in_progress": 1, "failed": 0 }, "phase": { - "current": "Pre-Deletion Safety Verification", + "current": "Delete Legacy Directories", "id": null, "total": 2 }, @@ -18,8 +18,8 @@ "max": 1 }, "session": { - "number": 3, + "number": 4, "started_at": "2026-01-25T06:18:19.396966" }, - "last_update": "2026-01-25T06:23:54.704974" + "last_update": "2026-01-25T06:27:40.436575" } \ No newline at end of file diff --git a/src/components-vite/Alert.tsx b/src/components-vite/Alert.tsx deleted file mode 100644 index 161c1b7..0000000 --- a/src/components-vite/Alert.tsx +++ /dev/null @@ -1,43 +0,0 @@ -// Alert.tsx -import React from 'react'; -import { motion } from 'framer-motion'; -import { CheckCircle2, XCircle } from 'lucide-react'; - -interface AlertProps { - variant?: 'success' | 'error'; - children: React.ReactNode; -} - -export const Alert: React.FC = ({ variant = 'success', children }) => { - const variants = { - success: { - bg: 'bg-primary/10', - border: 'border-primary', - text: 'text-primary', - icon: - }, - error: { - bg: 'bg-red-500/10', - border: 'border-red-500', - text: 'text-red-400', - icon: - } - }; - - const style = variants[variant]; - - return ( - - {style.icon} -
{children}
-
- ); -}; - -export const AlertDescription: React.FC<{ children: React.ReactNode }> = ({ children }) => { - return
{children}
; -}; \ No newline at end of file diff --git a/src/components-vite/Badge.tsx b/src/components-vite/Badge.tsx deleted file mode 100644 index 47fa69f..0000000 --- a/src/components-vite/Badge.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import * as React from "react" -import { cva, type VariantProps } from "class-variance-authority" -import { type ClassValue, clsx } from "clsx" -import { twMerge } from "tailwind-merge" - -// Fügen Sie diese Funktion direkt in die Datei ein -function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)) -} - -const badgeVariants = cva( - "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", - { - variants: { - variant: { - default: - "border-transparent bg-primary text-primary-foreground hover:bg-primary/80", - secondary: - "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", - destructive: - "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80", - outline: "text-foreground", - }, - }, - defaultVariants: { - variant: "default", - }, - } -) - -export interface BadgeProps - extends React.HTMLAttributes, - VariantProps {} - -function Badge({ className, variant, ...props }: BadgeProps) { - return ( -
- ) -} - -export { Badge, badgeVariants } diff --git a/src/components-vite/Breadcrumbs.tsx b/src/components-vite/Breadcrumbs.tsx deleted file mode 100644 index 672fbcd..0000000 --- a/src/components-vite/Breadcrumbs.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import React from 'react'; -import { Link, useLocation } from 'react-router-dom'; -import { ChevronRight } from 'lucide-react'; - -interface BreadcrumbsProps { - currentTitle?: string; -} - -const Breadcrumbs: React.FC = ({ currentTitle }) => { - const location = useLocation(); - - // Definiere die feste Struktur der Breadcrumbs - const generateBreadcrumbs = () => { - const segments = location.pathname.split('/').filter(Boolean); - - // Basis-Breadcrumb-Struktur - const breadcrumbs = []; - - // Portfolio ist immer der erste Level nach Home - if (segments.includes('portfolio')) { - breadcrumbs.push({ - title: 'Portfolio', - path: '/portfolio', - isLast: segments.length === 1 - }); - } - - // Wenn wir in einem Projekt sind, füge den Projekttitel hinzu - if (segments.length > 1 && currentTitle) { - breadcrumbs.push({ - title: currentTitle, - path: location.pathname, - isLast: true - }); - } - - return breadcrumbs; - }; - - const breadcrumbs = generateBreadcrumbs(); - - // Wenn wir nur auf der Homepage sind, zeigen wir keine Breadcrumbs - if (location.pathname === '/') return null; - - return ( - - ); -}; - -export default Breadcrumbs; diff --git a/src/components-vite/Button.tsx b/src/components-vite/Button.tsx deleted file mode 100644 index a5574d5..0000000 --- a/src/components-vite/Button.tsx +++ /dev/null @@ -1,60 +0,0 @@ -// Button.tsx -import React from 'react'; -import { Loader2 } from 'lucide-react'; - -interface ButtonProps extends React.ButtonHTMLAttributes { - isLoading?: boolean; - variant?: 'default' | 'outline' | 'ghost'; - size?: 'sm' | 'md' | 'lg'; - leftIcon?: React.ReactNode; - rightIcon?: React.ReactNode; -} - -export const Button = React.forwardRef( - ({ - className = '', - variant = 'default', - size = 'md', - isLoading, - leftIcon, - rightIcon, - children, - disabled, - ...props - }, ref) => { - const baseStyles = 'inline-flex items-center justify-center rounded-lg font-medium transition-all focus:outline-none focus:ring-2 focus:ring-primary disabled:opacity-50 disabled:cursor-not-allowed'; - - const variants = { - default: 'bg-primary text-white hover:bg-primary/90', - outline: 'border border-primary text-primary hover:bg-primary hover:text-white', - ghost: 'text-primary hover:bg-primary/10' - }; - - const sizes = { - sm: 'h-9 px-4 text-sm', - md: 'h-12 px-6 text-base', - lg: 'h-14 px-8 text-lg' - }; - - return ( - - ); - } -); - -Button.displayName = 'Button'; \ No newline at end of file diff --git a/src/components-vite/Card.tsx b/src/components-vite/Card.tsx deleted file mode 100644 index d520e1a..0000000 --- a/src/components-vite/Card.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import * as React from "react" -import { type ClassValue, clsx } from "clsx" -import { twMerge } from "tailwind-merge" - -// Fügen Sie diese Funktion direkt in die Datei ein -function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)) -} - -const Card = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)) -Card.displayName = "Card" - -const CardHeader = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)) -CardHeader.displayName = "CardHeader" - -const CardTitle = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -

-)) -CardTitle.displayName = "CardTitle" - -const CardDescription = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -

-)) -CardDescription.displayName = "CardDescription" - -const CardContent = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -

-)) -CardContent.displayName = "CardContent" - -const CardFooter = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)) -CardFooter.displayName = "CardFooter" - -export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } diff --git a/src/components-vite/ContactBar.tsx b/src/components-vite/ContactBar.tsx deleted file mode 100644 index de28189..0000000 --- a/src/components-vite/ContactBar.tsx +++ /dev/null @@ -1,36 +0,0 @@ -"use client" - -import { Phone, Mail, ArrowRight } from "lucide-react" -import { Link } from "react-router-dom" -import { motion } from "framer-motion" - -export function ContactBar() { - return ( - - - - ) -} - diff --git a/src/components-vite/ContactForm.tsx b/src/components-vite/ContactForm.tsx deleted file mode 100644 index ad7d1b1..0000000 --- a/src/components-vite/ContactForm.tsx +++ /dev/null @@ -1,125 +0,0 @@ -import React, { useState } from 'react'; -import { Send, AlertCircle } from 'lucide-react'; -import { getCsrfToken } from '../utils/csrf'; -import { rateLimiter } from '../utils/rateLimiting'; - -interface ContactFormProps { - onSubmit: (data: FormData) => Promise; -} - -const ContactForm: React.FC = ({ onSubmit }) => { - const [name, setName] = useState(''); - const [email, setEmail] = useState(''); - const [message, setMessage] = useState(''); - const [error, setError] = useState(null); - const [success, setSuccess] = useState(false); - const [loading, setLoading] = useState(false); - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - setError(null); - setLoading(true); - - try { - // Check rate limiting - const clientIp = '127.0.0.1'; // In production, get this from the request - if (rateLimiter.isRateLimited(clientIp)) { - const timeToReset = Math.ceil(rateLimiter.getTimeToReset(clientIp) / 1000 / 60); - throw new Error(`Too many attempts. Please try again in ${timeToReset} minutes.`); - } - - // Validate CSRF token - const formData = new FormData(); - formData.append('name', name); - formData.append('email', email); - formData.append('message', message); - formData.append('csrf_token', getCsrfToken()); - - await onSubmit(formData); - setSuccess(true); - setName(''); - setEmail(''); - setMessage(''); - } catch (err) { - setError(err instanceof Error ? err.message : 'An error occurred'); - } finally { - setLoading(false); - } - }; - - return ( -
- {error && ( -
- - {error} -
- )} - - {success && ( -
- Message sent successfully! -
- )} - -
- - setName(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" - required - /> -
- -
- - 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" - required - /> -
- -
- - -
- - -
- ); -}; - -export default ContactForm; \ No newline at end of file diff --git a/src/components-vite/ContactInfo.tsx b/src/components-vite/ContactInfo.tsx deleted file mode 100644 index 284a9ee..0000000 --- a/src/components-vite/ContactInfo.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { Phone, Mail, ArrowRight } from "lucide-react" -import { Link } from "react-router-dom" - -export function ContactInfo() { - return ( -
-
-

- Kontakt -

- - - - +1 234 567 890 - - - - - info@damjan-savic.com - - - - Kontakt aufnehmen - - -
-
- ) -} diff --git a/src/components-vite/CookieBanner.tsx b/src/components-vite/CookieBanner.tsx deleted file mode 100644 index 6750142..0000000 --- a/src/components-vite/CookieBanner.tsx +++ /dev/null @@ -1,490 +0,0 @@ -import { useEffect, useState, useCallback } from 'react'; -import { useTranslation } from 'react-i18next'; -import { initGA, logPageView, initFBPixel, initFunctionalCookies } from '../utils/analytics'; -import { loadGoogleTagManager } from '../services/gtm'; - -// Feste Übersetzungen direkt in der Komponente -const translations = { - de: { - title: "Cookie-Einstellungen", - message: "Wir nutzen Cookies zur Verbesserung der Website. Ohne Ihre Zustimmung keine Datenweitergabe an Dritte. ", - acceptAll: "Alle akzeptieren", - save: "Einstellungen speichern", - decline: "Ablehnen", - necessary: "Notwendige Cookies", - necessaryDesc: "Für grundlegende Funktionen der Website", - analytics: "Analyse-Cookies", - analyticsDesc: "Für Analysen zur Verbesserung der Website", - marketing: "Marketing-Cookies", - marketingDesc: "Für personalisierte Werbung und Inhalte", - functional: "Funktionale Cookies", - functionalDesc: "Für erweiterte Funktionen und Personalisierung", - learnMore: "Mehr erfahren", - alwaysActive: "Immer aktiv" - }, - en: { - title: "Cookie Settings", - message: "This website uses cookies to enhance your browsing experience. Data will not be shared with third parties without your consent.", - acceptAll: "Accept All", - save: "Save Settings", - decline: "Decline All", - necessary: "Necessary Cookies", - necessaryDesc: "For basic website functions", - analytics: "Analytics Cookies", - analyticsDesc: "For analyzing website usage", - marketing: "Marketing Cookies", - marketingDesc: "For personalized ads and content", - functional: "Functional Cookies", - functionalDesc: "For enhanced functionality and personalization", - learnMore: "Learn more", - alwaysActive: "Always active" - }, - sr: { - title: "Podešavanja kolačića", - message: "Ovaj sajt koristi kolačiće za bolje iskustvo pretraživanja. Podaci neće biti deljeni sa trećim licima bez vaše saglasnosti.", - acceptAll: "Prihvati sve", - save: "Sačuvaj podešavanja", - decline: "Odbij sve", - necessary: "Neophodni kolačići", - necessaryDesc: "Za osnovne funkcije sajta", - analytics: "Analitički kolačići", - analyticsDesc: "Za analizu korišćenja sajta", - marketing: "Marketing kolačići", - marketingDesc: "Za personalizovane reklame i sadržaj", - functional: "Funkcionalni kolačići", - functionalDesc: "Za napredne funkcije i personalizaciju", - learnMore: "Saznajte više", - alwaysActive: "Uvek aktivno" - } -}; - -// Definition der verschiedenen Services -const cookieServices = { - essentiell: { - category: 'necessary', - services: [ - { - name: 'Session Cookies', - provider: 'Eigentümer der Website', - purpose: 'Speichert Ihre Sitzungsinformationen' - } - ] - }, - funktional: { - category: 'functional', - services: [ - { - name: 'Spracheinstellungen', - provider: 'Eigentümer der Website', - purpose: 'Speichert Ihre bevorzugte Sprache' - }, - { - name: 'Google Fonts', - provider: 'Google LLC', - purpose: 'Anzeige von Webschriften' - } - ] - }, - analyse: { - category: 'analytics', - services: [ - { - name: 'Google Analytics', - provider: 'Google LLC', - purpose: 'Analysiert Webseitennutzung und Nutzerverhalten' - } - ] - }, - marketing: { - category: 'marketing', - services: [ - { - name: 'Google Ads', - provider: 'Google LLC', - purpose: 'Personalisierte Werbung' - }, - { - name: 'Facebook Pixel', - provider: 'Meta Platforms Inc.', - purpose: 'Tracking von Werbekonversionen' - } - ] - } -}; - -interface CookieSettings { - necessary: boolean; - analytics: boolean; - marketing: boolean; - functional: boolean; -} - -// Entfernt externe Google Fonts, falls keine Zustimmung vorliegt -const removeExternalGoogleFonts = () => { - // Suche nach allen Google Fonts Link-Elementen und entferne sie - const linkElements = document.querySelectorAll('link[href*="fonts.googleapis.com"]'); - linkElements.forEach(link => { - link.parentNode?.removeChild(link); - }); - - // Suche nach allen Google Fonts Script-Elementen und entferne sie - const scriptElements = document.querySelectorAll('script[src*="fonts.googleapis.com"]'); - scriptElements.forEach(script => { - script.parentNode?.removeChild(script); - }); -}; - -// Funktion zum Blockieren von Google Analytics falls noch keine Zustimmung vorliegt -const blockGoogleAnalytics = () => { - // Entferne alle bestehenden GA Cookies - document.cookie.split(';').forEach(function(c) { - if (c.trim().indexOf('_ga') === 0) { - document.cookie = c.trim().split('=')[0] + '=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/;'; - } - }); -}; - -const CookieBanner = () => { - const { i18n } = useTranslation(); - const [currentLang, setCurrentLang] = useState<'de' | 'en' | 'sr'>('de'); - const [showBanner, setShowBanner] = useState(false); - const [showSettings, setShowSettings] = useState(false); - const [cookieSettings, setCookieSettings] = useState({ - necessary: true, // Always true and disabled - analytics: false, - marketing: false, - functional: false - }); - - // Initialisiere Dienste basierend auf den Cookie-Einstellungen - const initializeServices = useCallback((settings: CookieSettings) => { - // Blockieren von Diensten, wenn nicht zugestimmt wurde - if (!settings.functional) { - removeExternalGoogleFonts(); - } - - if (!settings.analytics) { - blockGoogleAnalytics(); - } else { - // Initialisiere Google Analytics nur wenn ausdrücklich zugestimmt wurde - initGA(); - logPageView(); - } - - // Marketing-Dienste nur mit Zustimmung - if (settings.marketing) { - initFBPixel(); - loadGoogleTagManager(); - } - - // Funktionale Dienste nur mit Zustimmung - if (settings.functional) { - initFunctionalCookies(); - } - }, []); - - // Aktuelle Sprache erkennen - useEffect(() => { - const lang = i18n.language.substring(0, 2); - if (lang === 'de' || lang === 'en' || lang === 'sr') { - setCurrentLang(lang); - } else { - setCurrentLang('en'); // Fallback to English - } - }, [i18n.language]); - - // Get text based on current language - const getText = (key: keyof typeof translations.en) => { - return translations[currentLang][key]; - }; - - // DSGVO: Unmittelbar beim ersten Laden blockiere alle nicht-essentiellen Dienste - useEffect(() => { - // Blockiere externe Dienste bis Zustimmung vorliegt - removeExternalGoogleFonts(); - blockGoogleAnalytics(); - }, []); - - // Prüfe beim Laden, ob bereits Zustimmung vorhanden ist - useEffect(() => { - try { - const storedSettings = localStorage.getItem('cookieSettings'); - const hasConsent = localStorage.getItem('cookieConsent'); - - if (storedSettings && hasConsent) { - const parsedSettings = JSON.parse(storedSettings) as CookieSettings; - setCookieSettings(parsedSettings); - - // Lade die entsprechenden Dienste basierend auf den Einstellungen - initializeServices(parsedSettings); - setShowBanner(false); - } else { - // Zeige Banner wenn keine Zustimmung vorhanden ist - setShowBanner(true); - } - } catch (error) { - console.error('Error loading cookie settings:', error); - setShowBanner(true); - } - }, [initializeServices]); - - const handleSaveSettings = () => { - localStorage.setItem('cookieSettings', JSON.stringify(cookieSettings)); - localStorage.setItem('cookieConsent', 'true'); - localStorage.setItem('cookieConsentDate', new Date().toISOString()); - - // Initialisiere Dienste basierend auf den ausgewählten Einstellungen - initializeServices(cookieSettings); - - setShowSettings(false); - setShowBanner(false); - }; - - const handleAcceptAll = () => { - const allSettings: CookieSettings = { - necessary: true, - analytics: true, - marketing: true, - functional: true - }; - - localStorage.setItem('cookieSettings', JSON.stringify(allSettings)); - localStorage.setItem('cookieConsent', 'true'); - localStorage.setItem('cookieConsentDate', new Date().toISOString()); - setCookieSettings(allSettings); - - // Initialisiere alle Dienste - initializeServices(allSettings); - - setShowSettings(false); - setShowBanner(false); - }; - - const handleDeclineAll = () => { - const minimalSettings: CookieSettings = { - necessary: true, - analytics: false, - marketing: false, - functional: false - }; - - localStorage.setItem('cookieSettings', JSON.stringify(minimalSettings)); - localStorage.setItem('cookieConsent', 'true'); - localStorage.setItem('cookieConsentDate', new Date().toISOString()); - setCookieSettings(minimalSettings); - - // Block all non-essential services - initializeServices(minimalSettings); - - setShowSettings(false); - setShowBanner(false); - }; - - const toggleSetting = (setting: keyof Omit) => { - setCookieSettings({ - ...cookieSettings, - [setting]: !cookieSettings[setting] - }); - }; - - // If banner should not be shown, return null - if (!showBanner) { - return null; - } - - return ( -
-
-
- {!showSettings ? ( -
-
-

{getText('message')}

-
-
- - - -
-
- ) : ( -
-
-

{getText('title')}

- -
- -
-

{getText('message')}

- - {/* Cookie categories */} -
- {/* Necessary cookies - always enabled */} -
-
-
{getText('necessary')}
-
{getText('alwaysActive')}
-
-

{getText('necessaryDesc')}

- - {/* Services List */} -
- {cookieServices.essentiell.services.map((service, index) => ( -
-
{service.name}
-
Anbieter: {service.provider}
-
Zweck: {service.purpose}
-
- ))} -
-
- - {/* Analytics cookies */} -
-
-
{getText('analytics')}
- -
-

{getText('analyticsDesc')}

- - {/* Services List */} -
- {cookieServices.analyse.services.map((service, index) => ( -
-
{service.name}
-
Anbieter: {service.provider}
-
Zweck: {service.purpose}
-
- ))} -
-
- - {/* Marketing cookies */} -
-
-
{getText('marketing')}
- -
-

{getText('marketingDesc')}

- - {/* Services List */} -
- {cookieServices.marketing.services.map((service, index) => ( -
-
{service.name}
-
Anbieter: {service.provider}
-
Zweck: {service.purpose}
-
- ))} -
-
- - {/* Functional cookies */} -
-
-
{getText('functional')}
- -
-

{getText('functionalDesc')}

- - {/* Services List */} -
- {cookieServices.funktional.services.map((service, index) => ( -
-
{service.name}
-
Anbieter: {service.provider}
-
Zweck: {service.purpose}
-
- ))} -
-
-
-
- -
- - -
- - -
- )} -
-
-
- ); -}; - -export default CookieBanner; \ No newline at end of file diff --git a/src/components-vite/DebugOverlay.tsx b/src/components-vite/DebugOverlay.tsx deleted file mode 100644 index 8c73789..0000000 --- a/src/components-vite/DebugOverlay.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { useScrollContext } from './ScrollContext'; - -interface KeyboardEvent { - key: string; -} - -const DebugOverlay: React.FC = () => { - const [showDebug, setShowDebug] = useState(false); - const { isTransitioning } = useScrollContext(); - - useEffect(() => { - const handleKeyPress = (e: KeyboardEvent) => { - if (e.key === 'd') { - setShowDebug((prev: boolean) => !prev); - } - }; - - window.addEventListener('keydown', handleKeyPress); - return () => window.removeEventListener('keydown', handleKeyPress); - }, []); - - if (!showDebug) return null; - - return ( -
-
Transitioning: {isTransitioning ? 'Yes' : 'No'}
-
Body Overflow: {document.body.style.overflow}
-
Scroll Position: {window.scrollY}
-
View Height: {window.innerHeight}
- -
- ); -}; - -export default DebugOverlay; \ No newline at end of file diff --git a/src/components-vite/ErrorBoundary.tsx b/src/components-vite/ErrorBoundary.tsx deleted file mode 100644 index f8a7900..0000000 --- a/src/components-vite/ErrorBoundary.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { Component, ErrorInfo, ReactNode } from 'react'; -import { AlertTriangle, RefreshCw } from 'lucide-react'; - -interface Props { - children: ReactNode; - fallback?: ReactNode; -} - -interface State { - hasError: boolean; - error?: Error; -} - -class ErrorBoundary extends Component { - public state: State = { - hasError: false - }; - - public static getDerivedStateFromError(error: Error): State { - return { hasError: true, error }; - } - - public componentDidCatch(error: Error, errorInfo: ErrorInfo) { - console.error('Uncaught error:', error, errorInfo); - } - - private handleRetry = () => { - this.setState({ hasError: false, error: undefined }); - }; - - public render() { - if (this.state.hasError) { - if (this.props.fallback) { - return this.props.fallback; - } - - return ( -
-
- -

Something went wrong

-

- {this.state.error?.message || 'An unexpected error occurred'} -

- -
-
- ); - } - - return this.props.children; - } -} - -export default ErrorBoundary; \ No newline at end of file diff --git a/src/components-vite/FAQSection.tsx b/src/components-vite/FAQSection.tsx deleted file mode 100644 index 83ceeac..0000000 --- a/src/components-vite/FAQSection.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import React, { useState } from 'react'; -import { ChevronDown, ChevronUp } from 'lucide-react'; -import { useTranslation } from 'react-i18next'; -import { trackFAQInteraction } from '../services/gtm'; - -export function FAQSection() { - const { t } = useTranslation(); - const [openItem, setOpenItem] = useState(null); - - const faqs = t('faq.questions', { returnObjects: true }) as Array<{ - question: string; - answer: string; - category?: string; - }>; - - const toggleItem = (index: number) => { - const isOpening = openItem !== index; - const question = faqs[index]?.question || ''; - - // Track FAQ interaction - trackFAQInteraction(question, isOpening ? 'open' : 'close'); - - setOpenItem(prev => prev === index ? null : 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 ( -
- - - - ); -}; - -export default SEO; diff --git a/src/components-vite/ScrollAnimation.tsx b/src/components-vite/ScrollAnimation.tsx deleted file mode 100644 index 12c98f3..0000000 --- a/src/components-vite/ScrollAnimation.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { motion, useAnimation } from "framer-motion" -import { useEffect, useRef } from "react" - -interface ScrollAnimationProps { - children: React.ReactNode - delay?: number -} - -const ScrollAnimation: React.FC = ({ children, delay = 0 }) => { - const controls = useAnimation() - const ref = useRef(null) - - useEffect(() => { - const observer = new IntersectionObserver( - ([entry]) => { - if (entry.isIntersecting) { - controls.start({ opacity: 1, y: 0, transition: { duration: 0.5, delay: delay } }) - } - }, - { - threshold: 0.5, - }, - ) - - if (ref.current) { - observer.observe(ref.current) - } - - return () => { - if (ref.current) { - observer.unobserve(ref.current) - } - } - }, [controls, delay]) - - return ( - - {children} - - ) -} - -export default ScrollAnimation - diff --git a/src/components-vite/ScrollContext.tsx b/src/components-vite/ScrollContext.tsx deleted file mode 100644 index ba77b07..0000000 --- a/src/components-vite/ScrollContext.tsx +++ /dev/null @@ -1,56 +0,0 @@ -// ScrollContext.tsx -import React, { createContext, useContext, useState, useCallback } from 'react'; - -interface ScrollContextType { - isTransitioning: boolean; - setIsTransitioning: (value: boolean) => void; - isMenuOpen: boolean; - setIsMenuOpen: (value: boolean) => void; - lockScroll: () => void; - unlockScroll: () => void; -} - -const ScrollContext = createContext(undefined); - -export const ScrollProvider = ({ children }: { children: React.ReactNode }) => { - const [isTransitioning, setIsTransitioning] = useState(false); - const [isMenuOpen, setIsMenuOpen] = useState(false); - - const lockScroll = useCallback(() => { - const scrollY = window.scrollY; - document.body.style.position = 'fixed'; - document.body.style.top = `-${scrollY}px`; - document.body.style.width = '100%'; - }, []); - - const unlockScroll = useCallback(() => { - const scrollY = document.body.style.top; - document.body.style.position = ''; - document.body.style.top = ''; - document.body.style.width = ''; - window.scrollTo(0, parseInt(scrollY || '0') * -1); - }, []); - - return ( - - {children} - - ); -}; - -export const useScrollContext = () => { - const context = useContext(ScrollContext); - if (context === undefined) { - throw new Error('useScrollContext must be used within a ScrollProvider'); - } - return context; -}; \ No newline at end of file diff --git a/src/components-vite/ShareMenu.tsx b/src/components-vite/ShareMenu.tsx deleted file mode 100644 index 2e6ee15..0000000 --- a/src/components-vite/ShareMenu.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import React, { useState, useRef } from 'react'; -import { Share2, X, Twitter, Facebook, Linkedin, Link as LinkIcon } from 'lucide-react'; -import { useOnClickOutside } from '../hooks/useOnClickOutside'; - -interface ShareMenuProps { - title: string; - url: string; - description: string; -} - -const ShareMenu: React.FC = ({ title, url, description }) => { - const [isOpen, setIsOpen] = useState(false); - const [copyStatus, setCopyStatus] = useState<'idle' | 'copied'>('idle'); - const menuRef = useRef(null); - - useOnClickOutside(menuRef, () => setIsOpen(false)); - - const shareLinks = [ - { - name: 'Twitter', - icon: Twitter, - href: `https://twitter.com/intent/tweet?text=${encodeURIComponent(title)}&url=${encodeURIComponent(url)}`, - label: 'Share on Twitter' - }, - { - name: 'Facebook', - icon: Facebook, - href: `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(url)}`, - label: 'Share on Facebook' - }, - { - name: 'LinkedIn', - icon: Linkedin, - href: `https://www.linkedin.com/shareArticle?mini=true&url=${encodeURIComponent(url)}&title=${encodeURIComponent(title)}&summary=${encodeURIComponent(description)}`, - label: 'Share on LinkedIn' - }, - ]; - - const copyToClipboard = async () => { - try { - await navigator.clipboard.writeText(url); - setCopyStatus('copied'); - setTimeout(() => setCopyStatus('idle'), 2000); - } catch (err) { - console.error('Failed to copy:', err); - } - }; - - const handleKeyDown = (e: React.KeyboardEvent) => { - if (e.key === 'Escape') { - setIsOpen(false); - } - }; - - return ( -
- - - {isOpen && ( -
-
- {shareLinks.map((link) => ( - - - ))} - -
-
- )} -
- ); -}; - -export default ShareMenu \ No newline at end of file diff --git a/src/components-vite/SkeletonLoader.tsx b/src/components-vite/SkeletonLoader.tsx deleted file mode 100644 index 556597e..0000000 --- a/src/components-vite/SkeletonLoader.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react'; - -interface SkeletonProps { - className?: string; -} - -const Skeleton: React.FC = ({ className }) => ( -
-); - -export const BlogPostSkeleton = () => ( -
- - - -
- - - -
-
-); - -export const ProjectSkeleton = () => ( -
- -
- - - -
- -
- - - -
-
-); - -export const PortfolioSkeleton = () => ( -
- -
- {[1, 2, 3].map((i) => ( -
- -
- ))} -
-
-); \ No newline at end of file diff --git a/src/components-vite/SkillsMatrix.tsx b/src/components-vite/SkillsMatrix.tsx deleted file mode 100644 index 3db7543..0000000 --- a/src/components-vite/SkillsMatrix.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import React from 'react'; -import { motion } from 'framer-motion'; -import { useInView } from 'react-intersection-observer'; - -interface Skill { - name: string; - level: number; - category: string; - description: string; -} - -interface SkillsMatrixProps { - skills: Skill[]; -} - -const SkillsMatrix: React.FC = ({ skills }) => { - const [ref, inView] = useInView({ - triggerOnce: true, - threshold: 0.1, - }); - - const categories = Array.from(new Set(skills.map(skill => skill.category))); - - return ( -
- {categories.map((category, categoryIndex) => ( -
-

{category}

-
- {skills - .filter(skill => skill.category === category) - .map((skill, skillIndex) => ( -
-
-
- {skill.name} -
-

{skill.description}

-
-
- {skill.level}% -
-
- -
-
- ))} -
-
- ))} -
- ); -}; - -export default SkillsMatrix; \ No newline at end of file diff --git a/src/components-vite/SkipToContent.tsx b/src/components-vite/SkipToContent.tsx deleted file mode 100644 index e4c8cae..0000000 --- a/src/components-vite/SkipToContent.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import React, { useState } from 'react'; - -const SkipToContent: React.FC = () => { - const [isFocused, setIsFocused] = useState(false); - - return ( - setIsFocused(true)} - onBlur={() => setIsFocused(false)} - > - Skip to main content - - ); -}; - -export default SkipToContent \ No newline at end of file diff --git a/src/components-vite/TestimonialCard.tsx b/src/components-vite/TestimonialCard.tsx deleted file mode 100644 index 2e72507..0000000 --- a/src/components-vite/TestimonialCard.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import React from 'react'; -import { Star, Quote } from 'lucide-react'; -import ImageWithFallback from './ImageWithFallback'; - -interface TestimonialProps { - name: string; - role: string; - company: string; - image: string; - content: string; - rating: number; -} - -const TestimonialCard: React.FC = ({ - name, - role, - company, - image, - content, - rating -}) => { - return ( -
- -
- -
-

{name}

-

{role} at {company}

-
-
-
- {[...Array(5)].map((_, i) => ( - - ))} -
-

{content}

-
- ); -}; - -export default TestimonialCard; \ No newline at end of file diff --git a/src/components-vite/TextArea.tsx b/src/components-vite/TextArea.tsx deleted file mode 100644 index 35cddcf..0000000 --- a/src/components-vite/TextArea.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; - -interface TextareaProps extends React.TextareaHTMLAttributes { - error?: string; -} - -export const Textarea = React.forwardRef( - ({ className = '', error, ...props }, ref) => { - return ( -
-