Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 42 additions & 28 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.

# Sample workflow for building and deploying a Jekyll site to GitHub Pages
# This workflow builds and deploys a Vite site to GitHub Pages
name: Deploy Vite site to Pages

on:
# Runs on pushes targeting the default branch
# Runs on pushes to master and version branches
push:
branches: ["master"]
branches: ["master", "v1", "v2", "v3", "v4", "v5"]

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
contents: write
pages: write
id-token: write

Expand All @@ -27,39 +22,58 @@ concurrency:
cancel-in-progress: false

jobs:
# Build job
build:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'

- name: Install dependencies
run: npm ci
- name: Setup Pages
id: pages
uses: actions/configure-pages@v5

- name: Build with Vite
run: npm run build
env:
NODE_ENV: production
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: ./dist
VITE_BASE_PATH: ${{ github.ref_name == 'master' && '/' || format('/{0}/', github.ref_name) }}

# Add these debug steps:
- name: Debug - Show branch and base path
run: |
echo "Branch: ${{ github.ref_name }}"
echo "Base path: ${{ github.ref_name == 'master' && '/' || format('/{0}/', github.ref_name) }}"

- name: Debug - List dist contents
run: |
echo "Contents of dist directory:"
ls -la dist/
echo "File count:"
find dist -type f | wc -l

- name: Debug - Show destination
run: |
echo "Destination directory: ${{ steps.destination.outputs.dir }}"

- name: Set destination directory
id: destination
run: |
if [ "${{ github.ref_name }}" == "master" ]; then
echo "dir=" >> $GITHUB_OUTPUT
else
echo "dir=${{ github.ref_name }}" >> $GITHUB_OUTPUT
fi

# Deployment job
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./dist
destination_dir: ${{ steps.destination.outputs.dir }}
keep_files: true
force_orphan: false
10 changes: 10 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
},
"dependencies": {
"@tailwindcss/vite": "^4.1.18",
"lucide-react": "^0.562.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-icons": "^5.5.0",
Expand Down
Binary file added src/assets/gifs/dashboard.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/gifs/multimodal_ui.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/gifs/notilytics.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/gifs/portfolio.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
File renamed without changes
Binary file added src/assets/images/dashboard.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
Binary file added src/assets/images/methodology.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/images/multimodal.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/images/notilytics.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/images/portfolio.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
10 changes: 4 additions & 6 deletions src/components/AboutMe.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import profileImg from '../assets/cropped-profile.jpg'
import profileImg from '../assets/images/cropped-profile.jpg'
import resumePdf from '../assets/resume.pdf'

import { FaLinkedin, FaGithub, FaFileDownload } from 'react-icons/fa'
Expand All @@ -15,11 +15,9 @@ function AboutMe() {
<div>
<h1 className="text-6xl font-extrabold font-mono mb-6 text-white">Luan Tran</h1>
<p className="mb-6 text-white">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus et urna in quam vehicula pretium vitae sed lorem.
Phasellus egestas convallis euismod. Cras magna magna, vestibulum quis iaculis non, mattis vel nulla.
Praesent dictum at purus vel fringilla. Nulla feugiat tincidunt pulvinar. Donec posuere nisl vel molestie blandit.
Sed ornare velit eu vestibulum lacinia. Fusce et eros dui. Sed ante mi, ornare et cursus scelerisque, egestas et risus.
Etiam odio ex, maximus ac imperdiet vitae, bibendum ut ipsum.
I'm a Master's of Applied Computer Science student with a passion for machine learning and web development.
I've worked on projects ranging from English proficiency classification to reactive web applications. Currently, I'm exploring ways to integrate using LLMs as agents in the context
of linguistic education (French and English) and looking for opportunities in Web Development/Machine Learning.
</p>
<div className="flex justify-between items-center">
<ul className="flex gap-4">
Expand Down
54 changes: 42 additions & 12 deletions src/components/Experience.jsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,58 @@
import SectionArrow from "./SectionArrow.jsx";
import {experienceData} from "./data/experience.js";

const ExperienceItem = ({ data }) => {

return (
<div className="relative pl-12 pb-12">
<div className="absolute left-2.5 top-1.5 w-3 h-3 bg-green-500 rounded-full border-2 border-gray-900"></div>

<div className="bg-white/10 border border-gray-700 rounded-lg p-6 hover:border-gray-600 transition-colors">
<div className="flex items-start justify-between mb-3">
<div>
<span className="commit-hash text-yellow-400 text-sm">{data.hash}</span>
<h3 className="text-3xl font-semibold text-white mt-1">{data.title}</h3>
<h4 className="mt-1"><span className="px-2 py-1 bg-blue-500/20 text-blue-400 rounded text-md">{data.company}
</span> </h4>
</div>
<span className="text-gray-400 text-lg">{data.period}</span>
</div>
<div className="space-y-2 mb-4">
{data.achievements.map((achievementPoint, index) => (
<div key={index} className="text-green-400 text-md commit-hash">
<span className="mr-2">+</span>{achievementPoint}
</div>
))}
</div>
<div className="flex flex-wrap gap-2 pt-4 border-t border-white/50">
{data.tech.map((techPoint, index) => (
<span key={index} className="px-3 py-1 bg-gray-700 text-gray-300 rounded-full text-xs">{techPoint}</span>
))}
</div>
</div>
</div>
)
}

function Experience() {
return (
<section id="experience" className="min-h-screen flex flex-col items-center justify-between">
<div className="flex-grow flex items-center justify-center w-full">

<div className="max-w-6xl mx-auto px-4">
<div className="flex-grow flex items-center justify-center w-full ">
<div className="max-w-6xl mx-auto mb-16">
<h2 className="text-5xl font-bold text-white text-center mb-12">
Work Experience
</h2>

<div className="space-y-8">
<div className="bg-white/10 backdrop-blur-sm rounded-lg p-6">
</div>

<div className="bg-white/10 backdrop-blur-sm rounded-lg p-6">
</div>
<div className="relative">
<div className="absolute left-4 top-0 bottom-0 w-0.5 bg-gray-700"></div>

<div className="bg-white/10 backdrop-blur-sm rounded-lg p-6">
</div>
{experienceData.map((experience) => (
<ExperienceItem key={experience.hash} data={experience} />
))}
</div>
</div>
</div>
<SectionArrow targetSection="projects" />
<SectionArrow targetSection="projects"/>
</section>
)
}
Expand Down
142 changes: 133 additions & 9 deletions src/components/Projects.jsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,132 @@
import { ExternalLink, Code } from 'lucide-react';
import { projectData } from './data/projects.js';
import {useState} from "react";

const ProjectCard = ({ project }) => {
const [showGif, setShowGif] = useState(false);
const [imageLoaded, setImageLoaded] = useState(false);
return (
<div className="relative group">
{/* Animated Glow Border */}
<div className="absolute -inset-[1px] rounded-2xl bg-[conic-gradient(from_140deg,_rgba(244,244,245,0.16),_rgba(39,39,42,0.2),_rgba(148,163,184,0.35),_rgba(244,244,245,0.16))] opacity-0 blur-[1px] transition-opacity duration-300 group-hover:opacity-100" />

{/* Main Card Container */}
<div className="relative z-10 h-full rounded-2xl border border-zinc-800/80 bg-white/10 text-white shadow-[0_18px_45px_rgba(0,0,0,0.7)] backdrop-blur-md transition-transform duration-300 group-hover:-translate-y-1 overflow-hidden flex flex-col">

{/* Project Image */}
<div
className="relative w-full aspect-[3/2] overflow-hidden bg-gradient-to-br from-primary/10 via-accent/10 to-primary/10"
onMouseEnter={() => setShowGif(true)}
onMouseLeave={() => setShowGif(false)}
>
{/* Placeholder - shows while image loads */}
{!imageLoaded && (
<div className="absolute inset-0 flex items-center justify-center bg-zinc-800/50">
<div className="animate-pulse text-zinc-400">Loading...</div>
</div>
)}

{/* Actual Image */}
<img
src={showGif ? project.image : project.thumbnail}
alt={project.title}
loading="lazy"
decoding="async"
onLoad={() => setImageLoaded(true)}
className={`object-cover w-full h-full transition-all duration-300 ${
imageLoaded ? 'opacity-100' : 'opacity-0'
} ${showGif ? 'scale-105' : ''}`}
/>
</div>

{/* Card Header */}
<div className="flex flex-col space-y-1.5 p-6 pb-3">

{/* Category Badge */}
<div className="inline-flex items-center rounded-full border px-2.5 py-0.5 font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 border-zinc-600/70 bg-transparent text-[10px] font-mono uppercase tracking-[0.16em] text-zinc-300 whitespace-nowrap w-fit">
<Code className="mr-1 h-3 w-3" />
{project.category}
</div>

{/* Title + Description */}
<div className="space-y-1">
<h3 className="tracking-tight text-base md:text-lg font-semibold">
{project.title}
</h3>
<p className="text-xs md:text-sm text-zinc-400">
{project.description}
</p>
</div>

</div>

{/* Card Body */}
<div className="p-6 space-y-4 pt-1 flex-grow flex flex-col">

{/* Tech Stack Tags */}
<div className="flex flex-wrap gap-2">
{project.techStack.map((tech, index) => (
<div
key={index}
className="inline-flex items-center rounded-full border px-2.5 py-0.5 transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 border-transparent hover:bg-secondary/80 border-none bg-zinc-800/80 text-[11px] font-normal text-zinc-100"
>
{tech}
</div>
))}

</div>

{/* Feature List */}
<ul className="space-y-1.5 text-xs text-zinc-300">
{project.features.map((feature, index) => (
<li key={index} className="flex gap-2">
<span className="mt-[3px] h-[3px] w-[3px] rounded-full bg-zinc-400 flex-shrink-0" />
<span>{feature}</span>
</li>
))}
</ul>

<div className="flex-grow"></div>

{/* Footer */}
<div className="flex items-center justify-end pt-2 text-xs text-zinc-400 border-t border-zinc-800/50">

{/* Action Links */}
<div className="flex items-center gap-3">

{/* Live Demo Link (conditional) */}
{project.demoUrl && (
<a
href={project.demoUrl}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-1 text-[11px] font-medium text-zinc-100 hover:text-white transition-colors"
>
Live Demo <ExternalLink className="h-3.5 w-3.5" />
</a>
)}

{/* GitHub Code Link */}
{project.githubUrl && (
<a
href={project.githubUrl}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-1 text-[11px] font-medium text-zinc-400 hover:text-zinc-100 transition-colors"
>
Code <ExternalLink className="h-3.5 w-3.5" />
</a>
)}

</div>
</div>
</div>

</div>
</div>
);
};

function Projects() {
return (
<section id="projects" className="min-h-screen py-20 px-4">
Expand All @@ -6,15 +135,10 @@ function Projects() {
Projects
</h2>

<div className="space-y-8">
<div className="bg-white/10 backdrop-blur-sm rounded-lg p-6">
</div>

<div className="bg-white/10 backdrop-blur-sm rounded-lg p-6">
</div>

<div className="bg-white/10 backdrop-blur-sm rounded-lg p-6">
</div>
<div className="grid gap-6 md:grid-cols-2">
{projectData.map((project, index) => (
<ProjectCard key={index} project={project} />
))}
</div>
</div>
</section>
Expand Down
4 changes: 2 additions & 2 deletions src/components/data/education.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Import logos
import mcgillLogo from '../../assets/mcgill_logo.png'
import concordiaLogo from '../../assets/concordia_logo.png'
import mcgillLogo from '../../assets/images/mcgill_logo.png'
import concordiaLogo from '../../assets/images/concordia_logo.png'

export const educationData = [
{
Expand Down
Loading