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
1 change: 1 addition & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/icon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>luantran.github.io</title>
</head>
Expand Down
13 changes: 13 additions & 0 deletions public/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ function App() {

<AboutMe />
<Skills />
<Education />
<Experience />
<Projects />
<Experience />
<Education />
</main>

</div>
Expand Down
Binary file added src/assets/gifs/councilofclassifiers.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/images/councilofclassifiers.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 modified src/assets/resume.pdf
Binary file not shown.
11 changes: 9 additions & 2 deletions src/components/Education.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useLanguage } from '../contexts/useLanguage.js';
import { getText } from '../utils/translationHelpers';

const EducationItem = ({ data, index, language }) => {
const { university, logo, degree, date } = data;
const { university, logo, degree, date, honors} = data;

return (
<div className={`group relative ${index > 0 ? 'my-[5px] sm:-mt-10 md:-mt-12 lg:-mt-14 xl:-mt-16' : 'my-[5px] sm:my-[3px]'} flex w-full sm:w-1/2 justify-start sm:justify-end sm:pr-[22px] sm:odd:justify-start sm:odd:self-end sm:odd:pl-[22px] sm:odd:pr-0 md:pr-[30px] md:odd:pl-[30px]`}>
Expand Down Expand Up @@ -52,6 +52,14 @@ const EducationItem = ({ data, index, language }) => {
>
{getText(degree, language)}
</p>
{honors && (
<p
className="text-gray-300 mt-0.5 sm:mt-1 break-words"
style={{ fontSize: 'clamp(0.6rem, 1.2vw + 0.25rem, 1rem)', fontStyle: 'italic' }}
>
{getText(honors, language)}
</p>
)}
<time
className="text-gray-400 mt-0.5 sm:mt-1 md:mt-2 break-words"
style={{ fontSize: 'clamp(0.7rem, 1.2vw + 0.25rem, 1.125rem)' }}
Expand Down Expand Up @@ -90,7 +98,6 @@ function Education() {
</div>
</div>
</div>
<SectionArrow targetSection="experience" />
</section>
);
}
Expand Down
6 changes: 3 additions & 3 deletions src/components/Experience.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ 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 sm:px-6 lg:px-8 mb-12 sm:mb-16 w-full">
<h2 className="text-3xl sm:text-4xl lg:text-5xl font-bold text-white text-center mb-8 sm:mb-12">
<div className="max-w-screen-xl mx-auto px-4 sm:px-6 lg:px-8 mb-12 sm:mb-16 w-full">
<h2 className="text-3xl sm:text-4xl lg:text-5xl font-bold text-white text-center mb-8 sm:mb-12 mt-10">
{getText(sectionTitle, language)}
</h2>

Expand All @@ -69,7 +69,7 @@ function Experience() {
</div>
</div>
</div>
<SectionArrow targetSection="projects"/>
<SectionArrow targetSection="education"/>
</section>
)
}
Expand Down
12 changes: 6 additions & 6 deletions src/components/Navbar.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useState, useEffect } from 'react'
import { FaUser, FaLightbulb, FaGraduationCap, FaBriefcase, FaFolderOpen, FaBars, FaTimes } from 'react-icons/fa'
import {FaUser, FaLightbulb, FaGraduationCap, FaBriefcase, FaFolderOpen, FaBars, FaTimes, FaTools} from 'react-icons/fa'
import LanguageSwitcher from './LanguageSwitcher'
import { useLanguage } from '../contexts/useLanguage.js';
import { getText } from '../utils/translationHelpers'
Expand Down Expand Up @@ -37,17 +37,17 @@ function Navbar() {
const navLabels = {
about: { en: 'About', fr: 'À propos' },
skills: { en: 'Skills', fr: 'Compétences' },
education: { en: 'Education', fr: 'Éducation' },
projects: { en: 'Projects', fr: 'Projets' },
experience: { en: 'Experience', fr: 'Expérience' },
projects: { en: 'Projects', fr: 'Projets' }
education: { en: 'Education', fr: 'Éducation' }
}

const navItems = [
{ id: 'about', icon: FaUser, label: navLabels.about },
{ id: 'skills', icon: FaLightbulb, label: navLabels.skills },
{ id: 'education', icon: FaGraduationCap, label: navLabels.education },
{ id: 'skills', icon: FaTools, label: navLabels.skills },
{ id: 'projects', icon: FaFolderOpen, label: navLabels.projects },
{ id: 'experience', icon: FaBriefcase, label: navLabels.experience },
{ id: 'projects', icon: FaFolderOpen, label: navLabels.projects }
{ id: 'education', icon: FaGraduationCap, label: navLabels.education }
]

return (
Expand Down
10 changes: 6 additions & 4 deletions src/components/Projects.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { projectData } from './data/projects.js';
import { useState } from "react";
import { useLanguage } from '../contexts/useLanguage.js';
import { getText, getArray } from '../utils/translationHelpers';
import SectionArrow from "./SectionArrow.jsx";

const ProjectCard = ({ project, language }) => {
const [showGif, setShowGif] = useState(false);
Expand Down Expand Up @@ -43,8 +44,7 @@ const ProjectCard = ({ project, language }) => {
{/* Card Header */}
<div className="flex flex-col space-y-1.5 p-4 sm:p-6 pb-2 sm:pb-3">
{/* Category Badge */}
<div className="inline-flex items-center rounded-full border px-2 sm: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] sm:text-[12px] font-mono uppercase tracking-[0.16em] text-zinc-300 whitespace-nowrap w-fit">
{getText(project.category, language)}
<div className="inline-flex items-center rounded-full border px-2 sm: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-400/50 bg-transparent text-[10px] sm:text-[12px] font-mono uppercase tracking-[0.16em] text-zinc-300 whitespace-nowrap w-fit"> {getText(project.category, language)}
</div>

{/* Title + Description */}
Expand Down Expand Up @@ -126,18 +126,20 @@ function Projects() {

return (
<section id="projects" className="min-h-screen py-12 sm:py-16 lg:py-20 px-4 sm:px-6 lg:px-8">
<div className="max-w-6xl mx-auto">
<div className="max-w-8xl mx-auto mb-12">
<h2 className="text-3xl sm:text-4xl lg:text-5xl font-bold text-white text-center mb-8 sm:mb-12">
{getText(sectionTitle, language)}
</h2>

<div className="grid gap-4 sm:gap-6 grid-cols-1 md:grid-cols-2">
<div className="grid gap-4 sm:gap-6 grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
{projectData.map((project, index) => (
<ProjectCard key={index} project={project} language={language} />
))}
</div>
</div>
<SectionArrow targetSection="experience" />
</section>

);
}

Expand Down
4 changes: 2 additions & 2 deletions src/components/Skills.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ function Skills() {
return (
<section id="skills" 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 sm:px-6 lg:px-8 w-full">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 w-full">
<h2 className="text-3xl sm:text-4xl lg:text-5xl font-bold text-white text-center mb-8 sm:mb-12">
{getText(sectionTitle, language)}
</h2>
Expand All @@ -66,7 +66,7 @@ function Skills() {

</div>
</div>
<SectionArrow targetSection="education" />
<SectionArrow targetSection="projects" />
</section>
)
}
Expand Down
14 changes: 10 additions & 4 deletions src/components/data/education.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ export const educationData = [
degree: {
en: "Master's of Applied Computer Science",
fr: "Maîtrise en informatique appliquée"
}
},
honors: null
},
{
university: {
Expand All @@ -26,7 +27,8 @@ export const educationData = [
degree: {
en: "Bachelor's of Computer Engineering",
fr: "Baccalauréat en génie informatique"
}
},
honors: null
},
{
university: {
Expand All @@ -36,8 +38,12 @@ export const educationData = [
date: '2012-2014',
logo: cimfLogo,
degree: {
en: 'French Baccalaureate - Highest Honors',
fr: 'Baccalauréat français - Mention très bien'
en: 'French Baccalaureate',
fr: 'Baccalauréat français'
},
honors: {
en: 'Highest Honors',
fr: "Mention Très Bien (Excellence)"
}
}
]
20 changes: 19 additions & 1 deletion src/components/data/projects.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import councilPng from '../../assets/images/councilofclassifiers.png'
import councilGif from '../../assets/gifs/councilofclassifiers.gif'
import medicalImagingGif from '../../assets/gifs/multimodal_ui.gif';
import medicalImagingPng from '../../assets/images/multimodal.png';
import methodologyPng from '../../assets/images/methodology.png';
Expand All @@ -9,6 +11,22 @@ import portfolioGif from '../../assets/gifs/portfolio.gif';
import portfolioPng from '../../assets/images/portfolio.png';

export const projectData = [
{
title: "Council of Classifiers",
description: "CEFR text classification web app combining the models from 'One model to grade them all' to predict English proficiency levels (A1-C2). Aggregates predictions using majority voting and confidence-weighted averaging for robust level estimation.",
category: "NLP / Web Application",
techStack: ["Python", "Flask", "Google Cloud Run", "React", "PyTorch", "Transformers", "Docker", "HuggingFace",],
features: [
"Three-model ensemble architecture with individual model breakdowns, confidence scores, and probability distributions for each CEFR level",
"Flask REST API backend with React frontend for interactive text input and results visualization, dynamically loading pre-trained models from HuggingFace Hub",
"Models trained separately in the 'One model to grade them all' pipeline",
"Continuous deployment to Google Cloud Run using Cloud Build triggers from GitHub repository with containerized production environment"
],
image: councilGif,
thumbnail: councilPng,
demoUrl: "https://councilofclassifiers.luantran.dev/",
githubUrl: "https://github.com/luantran/CouncilOfClassifiers"
},
{
title: "One Model to Grade them All",
description: "Comparative study of ML approaches for automatic CEFR level classification of English learner writing. Trained on EFCamDAT corpus and evaluated on multiple out-of-domain corpora (Write & Improve, ICNALE, ASAG) to assess cross-corpus generalization.",
Expand All @@ -21,7 +39,7 @@ export const projectData = [
],
image: methodologyPng,
thumbnail: methodologyPng,
demoUrl: null,
demoUrl: "https://huggingface.co/collections/theluantran/cefr-classifiers-one-model-to-grade-them-all",
githubUrl: "https://github.com/luantran/One-model-to-grade-them-all"
},
{
Expand Down