This commit is contained in:
2025-01-22 20:58:52 -03:00
commit d25dae734c
20 changed files with 6328 additions and 0 deletions

View File

@@ -0,0 +1,43 @@
import React, { useState } from 'react';
interface GlitchyLinkProps {
text: string;
url: string;
type?: 'email' | 'link';
}
const GlitchyLink: React.FC<GlitchyLinkProps> = ({ text, url, type = 'link' }) => {
const [copied, setCopied] = useState(false);
const handleClick = async (e: React.MouseEvent) => {
if (type === 'email') {
e.preventDefault();
if (copied) return;
await navigator.clipboard.writeText(text);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
}
};
return (
<a
href={url}
target="_blank"
rel="noopener noreferrer"
onClick={handleClick}
className="group relative inline-block hover:text-green-400 transition-colors"
>
<span className="relative inline-block animate-glitch-1">
{copied ? 'Copied!' : text}
</span>
<span className="absolute top-0 left-0 w-full h-full opacity-0 group-hover:opacity-70 group-hover:animate-glitch-1">
{copied ? 'Copied!' : text}
</span>
<span className="absolute top-0 left-0 w-full h-full opacity-0 group-hover:opacity-70 group-hover:animate-glitch-2">
{copied ? 'Copied!' : text}
</span>
</a>
);
};
export default GlitchyLink;

View File

@@ -0,0 +1,60 @@
import React from 'react';
import { Mail, Github, Linkedin, User, Code, BookOpen, Building, MapPin } from 'lucide-react';
import GlitchyLink from './GlitchyLink';
const ProfileContent: React.FC = () => {
return (
<div className="flex-1 overflow-auto">
<div className="space-y-8">
<div className="flex items-center gap-2">
<User className="w-6 h-6" />
<h2 className="text-2xl font-bold">José Henrique Ivanchechen</h2>
</div>
<div className="flex items-center gap-2">
<Code className="w-6 h-6" />
<p className="text-lg">Software Engineer</p>
</div>
<div className="flex items-center gap-2">
<Building className="w-6 h-6" />
<p className="text-lg">EposNow</p>
</div>
<div className="flex items-center gap-2">
<MapPin className="w-6 h-6" />
<p className="text-lg">Curitiba, PR - Brazil</p>
</div>
<div className="flex items-center gap-2">
<BookOpen className="w-6 h-6" />
<p className="text-lg">5+ Years Experience</p>
</div>
<div className="mt-12 space-y-6">
<h3 className="text-xl font-semibold">&gt; Contact Info:</h3>
<div className="space-y-4 pl-6">
<div className="flex items-center gap-3">
<Mail className="w-5 h-5" />
<GlitchyLink
text="jose.henrique.ivan@gmail.com"
url="mailto:jose.henrique.ivan@gmail.com"
type="email"
/>
</div>
<div className="flex items-center gap-3">
<Github className="w-5 h-5" />
<GlitchyLink text="@ivanch" url="https://github.com/ivanch" />
</div>
<div className="flex items-center gap-3">
<Linkedin className="w-5 h-5" />
<GlitchyLink text="/in/joseivanch" url="https://linkedin.com/in/joseivanch" />
</div>
</div>
</div>
</div>
</div>
);
};
export default ProfileContent;

View File

@@ -0,0 +1,46 @@
import React from 'react';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import { FileWarning } from 'lucide-react';
interface ProjectsContentProps {
markdownPath: string;
}
const ProjectsContent: React.FC<ProjectsContentProps> = ({ markdownPath }) => {
const [content, setContent] = React.useState<string>('');
const [error, setError] = React.useState<string>('');
React.useEffect(() => {
fetch(markdownPath)
.then(response => {
if (!response.ok) throw new Error('Failed to load content');
return response.text();
})
.then(text => setContent(text))
.catch(err => setError(err.message));
}, [markdownPath]);
if (error) {
return (
<div className="flex-1 flex items-center justify-center">
<div className="text-center space-y-4">
<FileWarning className="w-12 h-12 mx-auto" />
<p className="text-lg">Error loading content: {error}</p>
</div>
</div>
);
}
return (
<div className="flex-1">
<div className="prose prose-invert prose-green max-w-none">
<ReactMarkdown remarkPlugins={[remarkGfm]}>
{content}
</ReactMarkdown>
</div>
</div>
);
};
export default ProjectsContent;

View File

@@ -0,0 +1,34 @@
import React from 'react';
import { ChevronRight } from 'lucide-react';
interface TerminalButtonProps {
children: React.ReactNode;
onClick?: () => void;
isSelected?: boolean;
}
const TerminalButton: React.FC<TerminalButtonProps> = ({ children, onClick, isSelected }) => {
const playKeyPress = () => {
const audio = new Audio('data:audio/wav;base64,UklGRnQGAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YU8GAACBhYqFbF1fdH2Dg4R/gIKFi4SAgX98eoCFhQAAhIWKhWxdX3R9g4OEf4CChYuEgIF/fHqAhYUAAP//');
audio.volume = 0.2;
audio.play().catch(() => {});
};
const handleClick = (e: React.MouseEvent) => {
playKeyPress();
onClick?.(e);
};
return (
<button
className={`terminal-button group ${isSelected ? 'terminal-button-selected' : ''}`}
onClick={handleClick}
>
<div className="glare-effect"></div>
<ChevronRight className="inline-block mr-2 w-4 h-4 transition-transform group-hover:translate-x-1" />
{children}
</button>
);
}
export default TerminalButton;