diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 2420e15..b169764 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -4,72 +4,16 @@ on: push: branches: [ "main" ] -env: - REGISTRY: ghcr.io - IMAGE_NAME: ${{ github.repository }} - jobs: - build-and-push: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Log in to the Container registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - tags: | - type=raw,value=latest - type=sha - - - name: Build and push Docker image - uses: docker/build-push-action@v5 - with: - context: . - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - deploy: runs-on: ubuntu-latest - needs: build-and-push steps: - - name: Deploy to Remote Server + - name: Deploy Application uses: appleboy/ssh-action@v1.0.3 with: host: ${{ secrets.REMOTE_HOST }} username: ${{ secrets.REMOTE_USER }} key: ${{ secrets.SSH_PRIVATE_KEY }} + passphrase: ${{ secrets.SSH_PASSPHRASE }} script: | - # Login to registry - echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin - - # Pull new image - docker pull ghcr.io/${{ env.IMAGE_NAME }}:latest - - # Stop and remove existing container - docker stop resume-frontend || true - docker rm resume-frontend || true - - # Run new container - docker run -d \ - --name resume-frontend \ - --restart unless-stopped \ - -p 80:80 \ - ghcr.io/${{ env.IMAGE_NAME }}:latest - - # Cleanup unused images - docker image prune -f + powershell -ExecutionPolicy Bypass -Command "Write-Host '=== Starting deployment ==='; if (Test-Path 'C:\projects\digital-resume-FE') { Set-Location 'C:\projects\digital-resume-FE'; git pull origin main } else { New-Item -ItemType Directory -Path 'C:\projects' -Force; Set-Location 'C:\projects'; git clone https://gitea.sashabayda.ca/Bayda77/digital-resume-FE.git }; Write-Host '=== Stopping container ==='; docker stop resume-frontend; docker rm resume-frontend; Write-Host '=== Building image ==='; Set-Location 'C:\projects\digital-resume-FE'; docker build -t resume-frontend:latest .; Write-Host '=== Running container ==='; docker run -d --name resume-frontend --network nginx_web --restart unless-stopped -p 3001:80 resume-frontend:latest; Write-Host '=== Verifying ==='; docker ps -a --filter name=resume-frontend" diff --git a/public/beszel.png b/public/beszel.png new file mode 100644 index 0000000..02a43d7 Binary files /dev/null and b/public/beszel.png differ diff --git a/public/fampic.jpg b/public/fampic.jpg new file mode 100644 index 0000000..198e57c Binary files /dev/null and b/public/fampic.jpg differ diff --git a/public/gangpic.jpg b/public/gangpic.jpg new file mode 100644 index 0000000..b785361 Binary files /dev/null and b/public/gangpic.jpg differ diff --git a/public/janepic.jpg b/public/janepic.jpg new file mode 100644 index 0000000..a6e4d9c Binary files /dev/null and b/public/janepic.jpg differ diff --git a/src/App.css b/src/App.css index 9ab18dd..fa0b6da 100644 --- a/src/App.css +++ b/src/App.css @@ -55,7 +55,99 @@ p { } /* Home page specific styles */ +.home-container { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + min-height: 80vh; + /* Takes up most of the viewport */ + text-align: center; + gap: 30px; + padding: 20px; +} + +.hero-profile-container { + padding: 10px; + background: rgba(255, 255, 255, 0.05); + /* Subtle backing */ + border-radius: 50%; + backdrop-filter: blur(10px); + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); + border: 1px solid rgba(255, 255, 255, 0.1); +} + +.hero-profile-img { + width: 200px; + height: 200px; + object-fit: cover; + border-radius: 50%; + /* Circle */ + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); + display: block; +} + +.hero-content { + display: flex; + flex-direction: column; + align-items: center; + gap: 15px; + color: white; + max-width: 700px; +} + +.hero-name { + font-size: clamp(40px, 6vw, 80px); + font-family: 'Roboto', sans-serif; + font-weight: 700; + color: white; + margin: 0; + letter-spacing: -0.5px; + text-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); +} + +.hero-bio { + font-size: clamp(16px, 1.5vw, 20px); + /* font-family: 'Roboto', sans-serif; - inherent*/ + font-weight: 300; + line-height: 1.6; + color: rgba(255, 255, 255, 0.9); + max-width: 600px; +} + +.hero-socials { + display: flex; + gap: 20px; + margin-top: 20px; + flex-wrap: wrap; + justify-content: center; +} + +.hero-social-link { + text-decoration: none; + color: rgba(255, 255, 255, 0.85); + font-size: 16px; + font-weight: 500; + padding: 10px 24px; + border: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 50px; + /* Pill shape */ + background: rgba(255, 255, 255, 0.05); + transition: all 0.3s ease; + backdrop-filter: blur(5px); +} + +.hero-social-link:hover { + background: rgba(255, 255, 255, 0.2); + color: white; + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + border-color: rgba(255, 255, 255, 0.5); +} + +/* Legacy Home styles (keep if referenced elsewhere, otherwise minimal) */ .hero-card { + /* Kept for backward compat if needed, but redesigned home won't use it */ border: 1px solid rgba(255, 255, 255, 0.2); border-radius: 10px; background: rgba(255, 255, 255, 0.1); @@ -318,6 +410,10 @@ p { width: 100%; padding: 20px; } + + .home-container { + padding-top: 50px; + } } @media (max-width: 768px) { @@ -329,6 +425,11 @@ p { .about-title { margin-bottom: 20px; } + + .home-container { + min-height: 70vh; + gap: 20px; + } } /* Projects Page Styles */ @@ -357,26 +458,31 @@ p { } .project-card { - background: rgba(255, 255, 255, 0.07); - backdrop-filter: blur(12px); - border: 1px solid rgba(255, 255, 255, 0.15); - border-radius: 16px; + background: rgba(255, 255, 255, 0.08); + /* Slightly lighter base */ + backdrop-filter: blur(16px); + /* Increased blur */ + border: 1px solid rgba(255, 255, 255, 0.2); + /* Softer border */ + border-radius: 24px; + /* More rounded */ padding: 25px; display: flex; flex-direction: column; transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); - /* Bouncy feel */ height: 100%; - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); + /* Softer shadow */ position: relative; overflow: hidden; } .project-card:hover { - background: rgba(255, 255, 255, 0.12); + background: rgba(255, 255, 255, 0.15); transform: translateY(-8px) scale(1.02); - box-shadow: 0 15px 30px rgba(0, 0, 0, 0.3); - border-color: rgba(255, 255, 255, 0.3); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2); + /* Deeper but soft shadow */ + border-color: rgba(255, 255, 255, 0.4); } .project-card::before { diff --git a/src/App.tsx b/src/App.tsx index a756f1c..03f99b4 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -7,6 +7,7 @@ import Home from './pages/Home'; import About from './pages/About'; import WorkExperience from './pages/WorkExperience'; import Projects from './pages/Projects'; +import Skills from './pages/Skills'; import Contact from './pages/Contact'; function ScrollToTop() { @@ -39,6 +40,7 @@ function App() { } /> } /> } /> + } /> } />
diff --git a/src/components/BioSection.tsx b/src/components/BioSection.tsx new file mode 100644 index 0000000..64c66b3 --- /dev/null +++ b/src/components/BioSection.tsx @@ -0,0 +1,100 @@ +import { motion, AnimatePresence } from "framer-motion"; +import { useState, useEffect } from "react"; + +interface BioSectionProps { + imageSrc: string; + imageAlt: string; + text: string; + reversed?: boolean; +} + +export default function BioSection({ imageSrc, imageAlt, text, reversed = false }: BioSectionProps) { + const images = imageSrc.split(',').map(src => src.trim()).filter(src => src.length > 0); + const [currentIndex, setCurrentIndex] = useState(0); + const [prevIndex, setPrevIndex] = useState(0); + const [direction, setDirection] = useState(1); // 1 for right, -1 for left (though we always slide right here) + + useEffect(() => { + if (images.length <= 1) return; + + const interval = setInterval(() => { + setPrevIndex(currentIndex); + setCurrentIndex((prev) => (prev + 1) % images.length); + }, 6000); + + return () => clearInterval(interval); + }, [images.length, currentIndex]); + + const slideVariants = { + enter: { + x: "-100%", + opacity: 1 + }, + center: { + x: 0, + opacity: 1 + }, + exit: { + x: "100%", + opacity: 1 + } + }; + + return ( +
+ + + + + + + +

+ {text} +

+
+
+ ); +} diff --git a/src/components/ParticlesBackground.tsx b/src/components/ParticlesBackground.tsx index 2e423d3..b5e2da6 100644 --- a/src/components/ParticlesBackground.tsx +++ b/src/components/ParticlesBackground.tsx @@ -35,31 +35,6 @@ const ParticlesBackground: React.FC = () => { this.x += this.speedX; this.y += this.speedY; - // update speedX - let speedXRng = Math.random(); - if (speedXRng > 0.75) { - this.speedX += Math.random() * 0.1; - } else if (speedXRng < 0.25) { - this.speedX -= Math.random() * 0.1; - } - if (this.speedX > 1) { - this.speedX = 1; - } else if (this.speedX < -1) { - this.speedX = -1; - } - - // update speedY - let speedYRng = Math.random(); - if (speedYRng > 0.75) { - this.speedY += Math.random() * 0.1; - } else if (speedYRng < 0.25) { - this.speedY -= Math.random() * 0.1; - } - if (this.speedY > 1) { - this.speedY = 1; - } else if (this.speedY < -1) { - this.speedY = -1; - } //size let sizeRng = Math.random(); diff --git a/src/components/ProjectCard.tsx b/src/components/ProjectCard.tsx new file mode 100644 index 0000000..ca8f926 --- /dev/null +++ b/src/components/ProjectCard.tsx @@ -0,0 +1,62 @@ +import { motion, Variants } from "framer-motion"; + +export interface Project { + id: number; + title: string; + description: string; + techStack: string[]; + image: string; + links: { + demo?: string; + repo?: string; + }; +} + +interface ProjectCardProps { + project: Project; + variants: Variants; +} + +export default function ProjectCard({ project, variants }: ProjectCardProps) { + return ( + +
+ {project.title} +
+
+

{project.title}

+
+ +
+ {project.techStack.map(tech => ( + {tech} + ))} +
+ +

+ {project.description} +

+ +
+ {project.links.demo && ( + + Live Demo + + )} + {project.links.repo && ( + + GitHub + + )} +
+
+ ); +} diff --git a/src/components/floatingHeader.tsx b/src/components/floatingHeader.tsx index 60f5eed..131654a 100644 --- a/src/components/floatingHeader.tsx +++ b/src/components/floatingHeader.tsx @@ -6,32 +6,38 @@ export default function FloatingHeader() { return (
diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 8f22df2..f0a207f 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -1,26 +1,11 @@ +import { motion } from "framer-motion"; import TypingText from "../components/animatedTyping"; -import FullPageImage from "../components/fullPageImage"; -import { delay, motion } from "framer-motion"; -// Animation and typing timing configuration -const ANIMATION_TIMINGS = { - // Animation delays (in seconds) - elementIn: 2.0, // When elements fade in +const welcomeText = `Hello! My name is Sasha Bayda and welcome to my digital resume site! - // Typing speeds (in milliseconds per character) - welcomeTextSpeed: 45, - nameSpeed: 120, +Here you will find some of my projects, skills, contact information and any information I couldn't fit into my resume. - // Typing delays (in milliseconds) - time before text starts typing - welcomeTextDelay: 2000, - nameDelay: 2000, - - // Test items animation (slides in from bottom) - testItemsStartDelay: 3.5, // When to start the first test item animation (in seconds) - testItemStaggerDelay: 0.2, // Delay between each test item (in seconds) - testItemAnimationDuration: 0.5, // Duration of each item's slide-in animation -}; -const welcomeText = "Hello! My name is Sasha Bayda and welcome to my digital resume site! Here you will find some of my projects, skills, contact information and any information I couldn't fit into my resume. Feel free to explore and learn more about me and if something isn't answered, don't hesitate to reach out via the contact page!"; +Feel free to explore and learn more about me and if something isn't answered, don't hesitate to reach out via the contact page!`; const CONTACT_LINKS = [ { label: "Email", url: "mailto:sasha.bayda@outlook.com" }, @@ -33,48 +18,55 @@ const CONTACT_LINKS = [ export default function Home() { return (
-
-
-
- - portfolio - -
- -
-
-
+ -
- + Sasha Bayda + + +
+
+
-
+ + {welcomeText} + + +
{CONTACT_LINKS.map((link, index) => ( {link.label} + > + {link.label} + ))}
diff --git a/src/pages/Projects.tsx b/src/pages/Projects.tsx index 2dfbd47..cf1feac 100644 --- a/src/pages/Projects.tsx +++ b/src/pages/Projects.tsx @@ -1,19 +1,8 @@ import { motion, Variants } from "framer-motion"; import FullPageImage from "../components/fullPageImage"; +import ProjectCard, { Project } from "../components/ProjectCard"; -interface Project { - id: number; - title: string; - description: string; - techStack: string[]; - image: string; // Ensure these images exist in public/ or use placeholders - links: { - demo?: string; - repo?: string; - }; -} - -const PROJECTS: Project[] = [ +const FEATURED_PROJECTS: Project[] = [ { id: 1, title: "Digital Resume", @@ -27,6 +16,29 @@ const PROJECTS: Project[] = [ }, ]; +const SIDEQUESTS: Project[] = [ + { + id: 101, // Different ID range for sidequests + title: "Experimental Shader", + description: "A WebGL shader experiment creating procedural textures and animations. Exploring noise functions and light interactions.", + techStack: ["WebGL", "GLSL", "React Three Fiber"], + image: "https://placehold.co/600x400/1a1a1a/cccccc?text=Shader+Experiment", // Placeholder image + links: { + repo: "https://github.com/Bayda77/sidequests", // Placeholder link + } + }, + { + id: 102, + title: "CLI Tool", + description: "A command-line utility for automating daily workflows and file management tasks.", + techStack: ["Rust", "Clap"], + image: "https://placehold.co/600x400/2a2a2a/dddddd?text=CLI+Tool", // Placeholder + links: { + repo: "https://github.com/Bayda77/cli-tools" + } + } +]; + const containerVariants: Variants = { hidden: { opacity: 0 }, visible: { @@ -55,63 +67,50 @@ export default function Projects() {
- - Featured Projects - - + {/* Featured Projects Section */} - {PROJECTS.map((project) => ( - -
- {project.title} -
-
-

{project.title}

-
+ + Featured Projects + -
- {project.techStack.map(tech => ( - {tech} - ))} -
+
+ {FEATURED_PROJECTS.map((project) => ( + + ))} +
+
-

- {project.description} -

-
- {project.links.demo && ( - - Live Demo - - )} - {project.links.repo && ( - - GitHub - - )} -
-
- ))} + {/* Sidequests Section */} + + + Sidequests + + +
+ {SIDEQUESTS.map((project) => ( + + ))} +
diff --git a/src/pages/Skills.tsx b/src/pages/Skills.tsx new file mode 100644 index 0000000..db6d8aa --- /dev/null +++ b/src/pages/Skills.tsx @@ -0,0 +1,170 @@ +import { motion, Variants } from "framer-motion"; + +interface SkillCategory { + category: string; + skills: string[]; +} + +interface ProfessionalSkill { + skill: string; + description: string; +} + +const TECHNICAL_SKILLS: SkillCategory[] = [ + { + category: "Languages", + skills: ["JavaScript (ES6+)", "TypeScript", "Python", "Java", "C", "C++", "Lua", "HTML", "CSS", "SQL"] + }, + { + category: "Frameworks/Libraries", + skills: ["React", "Express.js", "Django REST Framework", "AWS Lambda"] + }, + { + category: "Databases", + skills: ["PostgreSQL (SQL Based)", "CouchDB (No-SQL Based)"] + }, + { + category: "DevOps & Tools", + skills: ["Docker", "CircleCI", "Git (CLI)", "Postman", "AWS CDK", "Linux CLI"] + }, + { + category: "AWS Technologies", + skills: ["S3", "Cloudfront", "Route 53", "API Gateway", "Lambda", "RDS", "VPC", "ECS", "SQS"] + }, + { + category: "Concepts", + skills: ["RESTful APIs", "Agile/Scrum", "MVC Architecture", "Cloud Deployment", "Responsive Design"] + } +]; + +const PROFESSIONAL_SKILLS: ProfessionalSkill[] = [ + { + skill: "Customer Service & Sales", + description: "Clear communication and coordination with customers and clients" + }, + { + skill: "Technical Support", + description: "Diagnosing and resolving hardware/software issues across all major devices." + }, + { + skill: "Web Development", + description: "WordPress plugin development, front-end design, and site optimization." + }, + { + skill: "Scripting & Programming", + description: "JavaScript, Python, HTML/CSS, Git, and NPM package management." + }, + { + skill: "Collaboration & Problem-Solving", + description: "Working effectively with diverse teams to meet deadlines and ensure quality results." + }, + { + skill: "Adaptability", + description: "Quickly learning new tools, technologies, and processes in dynamic work environments." + } +]; + +const containerVariants: Variants = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { + staggerChildren: 0.1, + delayChildren: 0.2 + } + } +}; + +const itemVariants: Variants = { + hidden: { y: 20, opacity: 0 }, + visible: { + y: 0, + opacity: 1, + transition: { type: "spring", stiffness: 100 } + } +}; + +export default function Skills() { + return ( +
+
+ + +

+ Technical Skills +

+
+ {TECHNICAL_SKILLS.map((category) => ( + +

{category.category}

+
+ {category.skills.map((skill) => ( + + {skill} + + ))} +
+
+ ))} +
+
+ + +

+ Professional Skills +

+
+ {PROFESSIONAL_SKILLS.map((skill) => ( + +

{skill.skill}

+

+ {skill.description} +

+
+ ))} +
+
+
+
+ ); +}