diff --git a/index.html b/index.html index 73e2ffd..52e459e 100644 --- a/index.html +++ b/index.html @@ -2,6 +2,7 @@ + luantran.github.io diff --git a/public/icon.svg b/public/icon.svg new file mode 100644 index 0000000..f8ddc98 --- /dev/null +++ b/public/icon.svg @@ -0,0 +1,13 @@ + + + + + + { + + + L + + + } + \ No newline at end of file diff --git a/src/App.jsx b/src/App.jsx index 27717b1..e427e44 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -28,9 +28,9 @@ function App() { - - + + diff --git a/src/assets/gifs/councilofclassifiers.gif b/src/assets/gifs/councilofclassifiers.gif new file mode 100644 index 0000000..7057891 Binary files /dev/null and b/src/assets/gifs/councilofclassifiers.gif differ diff --git a/src/assets/images/councilofclassifiers.png b/src/assets/images/councilofclassifiers.png new file mode 100644 index 0000000..0b21296 Binary files /dev/null and b/src/assets/images/councilofclassifiers.png differ diff --git a/src/assets/resume.pdf b/src/assets/resume.pdf index 63b33e7..b87a4af 100644 Binary files a/src/assets/resume.pdf and b/src/assets/resume.pdf differ diff --git a/src/components/Education.jsx b/src/components/Education.jsx index ea18a46..aa06e78 100644 --- a/src/components/Education.jsx +++ b/src/components/Education.jsx @@ -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 (
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]`}> @@ -52,6 +52,14 @@ const EducationItem = ({ data, index, language }) => { > {getText(degree, language)}

+ {honors && ( +

+ {getText(honors, language)} +

+ )}
- ); } diff --git a/src/components/Experience.jsx b/src/components/Experience.jsx index a694f5b..1196639 100644 --- a/src/components/Experience.jsx +++ b/src/components/Experience.jsx @@ -55,8 +55,8 @@ function Experience() { return (
-
-

+
+

{getText(sectionTitle, language)}

@@ -69,7 +69,7 @@ function Experience() {

- +
) } diff --git a/src/components/Navbar.jsx b/src/components/Navbar.jsx index f69b6fe..bea84a5 100644 --- a/src/components/Navbar.jsx +++ b/src/components/Navbar.jsx @@ -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' @@ -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 ( diff --git a/src/components/Projects.jsx b/src/components/Projects.jsx index fd74e02..ecb68c6 100644 --- a/src/components/Projects.jsx +++ b/src/components/Projects.jsx @@ -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); @@ -43,8 +44,7 @@ const ProjectCard = ({ project, language }) => { {/* Card Header */}
{/* Category Badge */} -
- {getText(project.category, language)} +
{getText(project.category, language)}
{/* Title + Description */} @@ -126,18 +126,20 @@ function Projects() { return (
-
+

{getText(sectionTitle, language)}

-
+
{projectData.map((project, index) => ( ))}
+
+ ); } diff --git a/src/components/Skills.jsx b/src/components/Skills.jsx index 8f21f55..c735013 100644 --- a/src/components/Skills.jsx +++ b/src/components/Skills.jsx @@ -53,7 +53,7 @@ function Skills() { return (
-
+

{getText(sectionTitle, language)}

@@ -66,7 +66,7 @@ function Skills() {
- +
) } diff --git a/src/components/data/education.js b/src/components/data/education.js index 020afd6..55b28c0 100644 --- a/src/components/data/education.js +++ b/src/components/data/education.js @@ -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: { @@ -26,7 +27,8 @@ export const educationData = [ degree: { en: "Bachelor's of Computer Engineering", fr: "Baccalauréat en génie informatique" - } + }, + honors: null }, { university: { @@ -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)" } } ] \ No newline at end of file diff --git a/src/components/data/projects.js b/src/components/data/projects.js index f8473a8..ba22e7b 100644 --- a/src/components/data/projects.js +++ b/src/components/data/projects.js @@ -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'; @@ -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.", @@ -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" }, {