mirror of
https://github.com/BreizhHardware/portfolio.git
synced 2026-01-18 16:37:22 +01:00
feat: Add GitHubStatsSection component for displaying GitHub statistics
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
<html lang="fr">
|
<html lang="fr">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/V5.png" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name="generator" content="React" />
|
<meta name="generator" content="React" />
|
||||||
<meta name="theme-color" content="#000000" />
|
<meta name="theme-color" content="#000000" />
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
"@testing-library/jest-dom": "^6.8.0",
|
"@testing-library/jest-dom": "^6.8.0",
|
||||||
"@testing-library/react": "^16.3.0",
|
"@testing-library/react": "^16.3.0",
|
||||||
"@testing-library/user-event": "^14.6.1",
|
"@testing-library/user-event": "^14.6.1",
|
||||||
"@types/react": "19.1.11",
|
"@types/react": "19.1.12",
|
||||||
"@types/react-dom": "19.1.8",
|
"@types/react-dom": "19.1.9",
|
||||||
"aos": "^2.3.4",
|
"aos": "^2.3.4",
|
||||||
"browserslist": "^4.25.3",
|
"browserslist": "^4.25.3",
|
||||||
"browserslist-useragent": "^4.0.0",
|
"browserslist-useragent": "^4.0.0",
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
||||||
"@simonsmith/cypress-image-snapshot": "^10.0.2",
|
"@simonsmith/cypress-image-snapshot": "^10.0.2",
|
||||||
"@vitejs/plugin-react": "^5.0.1",
|
"@vitejs/plugin-react": "^5.0.2",
|
||||||
"prettier": "^3.6.2",
|
"prettier": "^3.6.2",
|
||||||
"vite": "^7.1.3",
|
"vite": "^7.1.3",
|
||||||
"vite-plugin-svgr": "^4.5.0",
|
"vite-plugin-svgr": "^4.5.0",
|
||||||
|
|||||||
48
pnpm-lock.yaml
generated
48
pnpm-lock.yaml
generated
@@ -22,16 +22,16 @@ importers:
|
|||||||
version: 6.8.0
|
version: 6.8.0
|
||||||
'@testing-library/react':
|
'@testing-library/react':
|
||||||
specifier: ^16.3.0
|
specifier: ^16.3.0
|
||||||
version: 16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.1.8(@types/react@19.1.11))(@types/react@19.1.11)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
version: 16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||||
'@testing-library/user-event':
|
'@testing-library/user-event':
|
||||||
specifier: ^14.6.1
|
specifier: ^14.6.1
|
||||||
version: 14.6.1(@testing-library/dom@10.4.1)
|
version: 14.6.1(@testing-library/dom@10.4.1)
|
||||||
'@types/react':
|
'@types/react':
|
||||||
specifier: 19.1.11
|
specifier: 19.1.12
|
||||||
version: 19.1.11
|
version: 19.1.12
|
||||||
'@types/react-dom':
|
'@types/react-dom':
|
||||||
specifier: 19.1.8
|
specifier: 19.1.9
|
||||||
version: 19.1.8(@types/react@19.1.11)
|
version: 19.1.9(@types/react@19.1.12)
|
||||||
aos:
|
aos:
|
||||||
specifier: ^2.3.4
|
specifier: ^2.3.4
|
||||||
version: 2.3.4
|
version: 2.3.4
|
||||||
@@ -94,8 +94,8 @@ importers:
|
|||||||
specifier: ^10.0.2
|
specifier: ^10.0.2
|
||||||
version: 10.0.2(cypress@15.0.0)
|
version: 10.0.2(cypress@15.0.0)
|
||||||
'@vitejs/plugin-react':
|
'@vitejs/plugin-react':
|
||||||
specifier: ^5.0.1
|
specifier: ^5.0.2
|
||||||
version: 5.0.1(vite@7.1.3(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.91.0))
|
version: 5.0.2(vite@7.1.3(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.91.0))
|
||||||
prettier:
|
prettier:
|
||||||
specifier: ^3.6.2
|
specifier: ^3.6.2
|
||||||
version: 3.6.2
|
version: 3.6.2
|
||||||
@@ -539,8 +539,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==}
|
resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==}
|
||||||
engines: {node: '>= 10.0.0'}
|
engines: {node: '>= 10.0.0'}
|
||||||
|
|
||||||
'@rolldown/pluginutils@1.0.0-beta.32':
|
'@rolldown/pluginutils@1.0.0-beta.34':
|
||||||
resolution: {integrity: sha512-QReCdvxiUZAPkvp1xpAg62IeNzykOFA6syH2CnClif4YmALN1XKpB39XneL80008UbtMShthSVDKmrx05N1q/g==}
|
resolution: {integrity: sha512-LyAREkZHP5pMom7c24meKmJCdhf2hEyvam2q0unr3or9ydwDL+DJ8chTF6Av/RFPb3rH8UFBdMzO5MxTZW97oA==}
|
||||||
|
|
||||||
'@rollup/pluginutils@5.2.0':
|
'@rollup/pluginutils@5.2.0':
|
||||||
resolution: {integrity: sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==}
|
resolution: {integrity: sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==}
|
||||||
@@ -889,13 +889,13 @@ packages:
|
|||||||
'@types/pixelmatch@5.2.6':
|
'@types/pixelmatch@5.2.6':
|
||||||
resolution: {integrity: sha512-wC83uexE5KGuUODn6zkm9gMzTwdY5L0chiK+VrKcDfEjzxh1uadlWTvOmAbCpnM9zx/Ww3f8uKlYQVnO/TrqVg==}
|
resolution: {integrity: sha512-wC83uexE5KGuUODn6zkm9gMzTwdY5L0chiK+VrKcDfEjzxh1uadlWTvOmAbCpnM9zx/Ww3f8uKlYQVnO/TrqVg==}
|
||||||
|
|
||||||
'@types/react-dom@19.1.8':
|
'@types/react-dom@19.1.9':
|
||||||
resolution: {integrity: sha512-xG7xaBMJCpcK0RpN8jDbAACQo54ycO6h4dSSmgv8+fu6ZIAdANkx/WsawASUjVXYfy+J9AbUpRMNNEsXCDfDBQ==}
|
resolution: {integrity: sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@types/react': ^19.0.0
|
'@types/react': ^19.0.0
|
||||||
|
|
||||||
'@types/react@19.1.11':
|
'@types/react@19.1.12':
|
||||||
resolution: {integrity: sha512-lr3jdBw/BGj49Eps7EvqlUaoeA0xpj3pc0RoJkHpYaCHkVK7i28dKyImLQb3JVlqs3aYSXf7qYuWOW/fgZnTXQ==}
|
resolution: {integrity: sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==}
|
||||||
|
|
||||||
'@types/sinonjs__fake-timers@8.1.1':
|
'@types/sinonjs__fake-timers@8.1.1':
|
||||||
resolution: {integrity: sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==}
|
resolution: {integrity: sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==}
|
||||||
@@ -915,8 +915,8 @@ packages:
|
|||||||
'@types/yauzl@2.10.3':
|
'@types/yauzl@2.10.3':
|
||||||
resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==}
|
resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==}
|
||||||
|
|
||||||
'@vitejs/plugin-react@5.0.1':
|
'@vitejs/plugin-react@5.0.2':
|
||||||
resolution: {integrity: sha512-DE4UNaBXwtVoDJ0ccBdLVjFTWL70NRuWNCxEieTI3lrq9ORB9aOCQEKstwDXBl87NvFdbqh/p7eINGyj0BthJA==}
|
resolution: {integrity: sha512-tmyFgixPZCx2+e6VO9TNITWcCQl8+Nl/E8YbAyPVv85QCc7/A3JrdfG2A8gIzvVhWuzMOVrFW1aReaNxrI6tbw==}
|
||||||
engines: {node: ^20.19.0 || >=22.12.0}
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0
|
vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0
|
||||||
@@ -2773,7 +2773,7 @@ snapshots:
|
|||||||
'@parcel/watcher-win32-ia32': 2.5.1
|
'@parcel/watcher-win32-ia32': 2.5.1
|
||||||
'@parcel/watcher-win32-x64': 2.5.1
|
'@parcel/watcher-win32-x64': 2.5.1
|
||||||
|
|
||||||
'@rolldown/pluginutils@1.0.0-beta.32': {}
|
'@rolldown/pluginutils@1.0.0-beta.34': {}
|
||||||
|
|
||||||
'@rollup/pluginutils@5.2.0(rollup@4.48.1)':
|
'@rollup/pluginutils@5.2.0(rollup@4.48.1)':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -3025,15 +3025,15 @@ snapshots:
|
|||||||
picocolors: 1.1.1
|
picocolors: 1.1.1
|
||||||
redent: 3.0.0
|
redent: 3.0.0
|
||||||
|
|
||||||
'@testing-library/react@16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.1.8(@types/react@19.1.11))(@types/react@19.1.11)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
|
'@testing-library/react@16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.28.3
|
'@babel/runtime': 7.28.3
|
||||||
'@testing-library/dom': 10.4.1
|
'@testing-library/dom': 10.4.1
|
||||||
react: 19.1.1
|
react: 19.1.1
|
||||||
react-dom: 19.1.1(react@19.1.1)
|
react-dom: 19.1.1(react@19.1.1)
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/react': 19.1.11
|
'@types/react': 19.1.12
|
||||||
'@types/react-dom': 19.1.8(@types/react@19.1.11)
|
'@types/react-dom': 19.1.9(@types/react@19.1.12)
|
||||||
|
|
||||||
'@testing-library/user-event@14.6.1(@testing-library/dom@10.4.1)':
|
'@testing-library/user-event@14.6.1(@testing-library/dom@10.4.1)':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -3093,11 +3093,11 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 24.3.0
|
'@types/node': 24.3.0
|
||||||
|
|
||||||
'@types/react-dom@19.1.8(@types/react@19.1.11)':
|
'@types/react-dom@19.1.9(@types/react@19.1.12)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/react': 19.1.11
|
'@types/react': 19.1.12
|
||||||
|
|
||||||
'@types/react@19.1.11':
|
'@types/react@19.1.12':
|
||||||
dependencies:
|
dependencies:
|
||||||
csstype: 3.1.3
|
csstype: 3.1.3
|
||||||
|
|
||||||
@@ -3118,12 +3118,12 @@ snapshots:
|
|||||||
'@types/node': 24.3.0
|
'@types/node': 24.3.0
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@vitejs/plugin-react@5.0.1(vite@7.1.3(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.91.0))':
|
'@vitejs/plugin-react@5.0.2(vite@7.1.3(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.91.0))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.28.3
|
'@babel/core': 7.28.3
|
||||||
'@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.3)
|
'@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.3)
|
||||||
'@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.3)
|
'@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.3)
|
||||||
'@rolldown/pluginutils': 1.0.0-beta.32
|
'@rolldown/pluginutils': 1.0.0-beta.34
|
||||||
'@types/babel__core': 7.20.5
|
'@types/babel__core': 7.20.5
|
||||||
react-refresh: 0.17.0
|
react-refresh: 0.17.0
|
||||||
vite: 7.1.3(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.91.0)
|
vite: 7.1.3(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(sass@1.91.0)
|
||||||
|
|||||||
BIN
public/V5.png
Normal file
BIN
public/V5.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 253 KiB |
@@ -1,59 +1,102 @@
|
|||||||
{
|
{
|
||||||
"about.title": "About me",
|
"about": {
|
||||||
"about.description": "I am a fourth-year student at ISEN Nantes, currently in a work-study program at Horoquartz. I am passionate about computer science. I learned to code on my own and am currently learning Rust and Go. I am also passionate about electronics and hardware. I have a homelab consisting of 3 servers: a DELL T320, a DELL T330, and a Dell Precision T3610, all running Proxmox.",
|
"title": "About me",
|
||||||
"card.title": "Fourth year student at ISEN Nantes in work-study program at Horoquartz",
|
"description": "I am a fourth-year student at ISEN Nantes, currently in a work-study program at Horoquartz. I am passionate about computer science. I learned to code on my own and am currently learning Rust and Go. I am also passionate about electronics and hardware. I have a homelab consisting of 3 servers: a DELL T320, a DELL T330, and a Dell Precision T3610, all running Proxmox."
|
||||||
"projects.title": "My projects",
|
},
|
||||||
"projects.Front end starter.description": "My personal starter for front end projects",
|
"card": {
|
||||||
"projects.Project C - ISEN CIR 1.description": "End of 1st year project at ISEN Nantes",
|
"title": "Fourth year student at ISEN Nantes in work-study program at Horoquartz"
|
||||||
"projects.Projet robot.description": "Robot project with the Modelec ISEN club for the French robotics cup (Development and deployment on Raspberry Pi)",
|
},
|
||||||
"projects.MercuryCloud.description": "Game server and VPS hosting project. Technical support position, administrator of VPS, Game and web services.",
|
"projects": {
|
||||||
"projects.Projet C++ - ISEN CIR 2.description": "End of 4th semester project at ISEN Nantes. Creation of a Tower Defense type game in C++ with the QT6 library.",
|
"title": "My projects",
|
||||||
"projects.Github NTFY.description": "Notification project for github and dockerhub releases that sends notifications on ntfy, gotify and discord.",
|
"Front end starter.description": "My personal starter for front end projects",
|
||||||
"projects.Projet C++ - ISEN CIPA 3.description": "Fish school behavior simulation game project in C++ with the SLD2 library, with multiplayer support.",
|
"Project C - ISEN CIR 1.description": "End of 1st year project at ISEN Nantes",
|
||||||
"projects.Alternance Horoquartz.description": "Development of an update system for Horoquartz products.",
|
"Projet robot.description": "Robot project with the Modelec ISEN club for the French robotics cup (Development and deployment on Raspberry Pi)",
|
||||||
"cv.title": "My resume",
|
"MercuryCloud.description": "Game server and VPS hosting project. Technical support position, administrator of VPS, Game and web services.",
|
||||||
"cv.path": "/CV-Felix-MARQUET-English.pdf",
|
"Projet C++ - ISEN CIR 2.description": "End of 4th semester project at ISEN Nantes. Creation of a Tower Defense type game in C++ with the QT6 library.",
|
||||||
"nav.about": "About me",
|
"Github NTFY.description": "Notification project for github and dockerhub releases that sends notifications on ntfy, gotify and discord.",
|
||||||
"nav.projects": "My projects",
|
"Projet C++ - ISEN CIPA 3.description": "Fish school behavior simulation game project in C++ with the SLD2 library, with multiplayer support.",
|
||||||
"nav.cv": "My resume",
|
"Alternance Horoquartz.description": "Development of an update system for Horoquartz products."
|
||||||
"skills.beginner": "Beginner",
|
},
|
||||||
"skills.intermediate": "Intermediate",
|
"cv": {
|
||||||
"skills.expert": "Expert",
|
"title": "My resume",
|
||||||
"skills.examples.C.title": "C Projects",
|
"path": "/CV-Felix-MARQUET-English.pdf"
|
||||||
"skills.examples.C.description": "End-of-first-year project at ISEN, basic algorithms, creation of a REST API.",
|
},
|
||||||
"skills.examples.C++.title": "C++ Projects",
|
"nav": {
|
||||||
"skills.examples.C++.description": "Tower Defense in Qt6, fish school simulation with SDL2 featuring multiplayer support, basic algorithms.",
|
"about": "About me",
|
||||||
"skills.examples.Admin Système.title": "System Administration",
|
"projects": "My projects",
|
||||||
"skills.examples.Admin Système.description": "Configuration of 3 DELL servers under Proxmox, virtualization, maintenance of virtual machines, deployment of applications in Azure.",
|
"cv": "My resume",
|
||||||
"skills.examples.Python.title": "Python Projects",
|
"contact": "Contact",
|
||||||
"skills.examples.Python.description": "GitHub NTFY for GitHub and DockerHub release notifications, course projects.",
|
"experience": "Experience",
|
||||||
"skills.examples.PHP.title": "PHP Projects",
|
"github": "GitHub"
|
||||||
"skills.examples.PHP.description": "Web development with PHP, AJAX, PostgreSQL.",
|
},
|
||||||
"skills.examples.HTML/CSS.title": "Front-end development",
|
"skills": {
|
||||||
"skills.examples.HTML/CSS.description": "Custom front-end starter, base project, various web projects.",
|
"beginner": "Beginner",
|
||||||
"skills.examples.JS/TS.title": "JavaScript/TypeScript",
|
"intermediate": "Intermediate",
|
||||||
"skills.examples.JS/TS.description": "React development, this portfolio, NodeJS API with Express, creation of the Studysen mobile application with React Native, professional projects.",
|
"expert": "Expert",
|
||||||
"skills.examples.Linux.title": "Linux Administration",
|
"examples": {
|
||||||
"skills.examples.Linux.description": "Server configuration, system administration, Ansible playbooks.",
|
"C": {
|
||||||
"skills.examples.Go.title": "Go Projects",
|
"title": "C Projects",
|
||||||
"skills.examples.Go.description": "Introduction to the language, creation of a REST API with Fiber, Go Gin, and the standard library, professional projects.",
|
"description": "End-of-first-year project at ISEN, basic algorithms, creation of a REST API."
|
||||||
"skills.examples.Docker.title": "Containerization",
|
},
|
||||||
"skills.examples.Docker.description": "Container deployment, Docker Compose configuration, image creation, high-availability service deployment via Swarm and Kubernetes.",
|
"C++": {
|
||||||
"skills.examples.Rust.title": "Rust Projects",
|
"title": "C++ Projects",
|
||||||
"skills.examples.Rust.description": "Learning the language, creation of a REST API with Ntex, rewrite of the GitHub NTFY API in Rust.",
|
"description": "Tower Defense in Qt6, fish school simulation with SDL2 featuring multiplayer support, basic algorithms."
|
||||||
"skills.examples.React.title": "React Development",
|
},
|
||||||
"skills.examples.React.description": "This portfolio, Modelec website, Studysen mobile application, professional projects.",
|
"Admin Système": {
|
||||||
|
"title": "System Administration",
|
||||||
"contact.title": "Contact",
|
"description": "Configuration of 3 DELL servers under Proxmox, virtualization, maintenance of virtual machines, deployment of applications in Azure."
|
||||||
"contact.subtitle": "A question? A project? Feel free to contact me!",
|
},
|
||||||
"contact.info.title": "Contact information",
|
"Python": {
|
||||||
"contact.info.email": "Email",
|
"title": "Python Projects",
|
||||||
"contact.info.status": "Status",
|
"description": "GitHub NTFY for GitHub and DockerHub release notifications, course projects."
|
||||||
"contact.info.statusValue": "ISEN student - Work-study at Horoquartz",
|
},
|
||||||
"contact.info.response": "Response",
|
"PHP": {
|
||||||
"contact.info.responseValue": "Usually within 24h",
|
"title": "PHP Projects",
|
||||||
"contact.subjects.title": "Topics of interest",
|
"description": "Web development with PHP, AJAX, PostgreSQL."
|
||||||
"contact.subjects.list": [
|
},
|
||||||
|
"HTML/CSS": {
|
||||||
|
"title": "Front-end development",
|
||||||
|
"description": "Custom front-end starter, base project, various web projects."
|
||||||
|
},
|
||||||
|
"JS/TS": {
|
||||||
|
"title": "JavaScript/TypeScript",
|
||||||
|
"description": "React development, this portfolio, NodeJS API with Express, creation of the Studysen mobile application with React Native, professional projects."
|
||||||
|
},
|
||||||
|
"Linux": {
|
||||||
|
"title": "Linux Administration",
|
||||||
|
"description": "Server configuration, system administration, Ansible playbooks."
|
||||||
|
},
|
||||||
|
"Go": {
|
||||||
|
"title": "Go Projects",
|
||||||
|
"description": "Introduction to the language, creation of a REST API with Fiber, Go Gin, and the standard library, professional projects."
|
||||||
|
},
|
||||||
|
"Docker": {
|
||||||
|
"title": "Containerization",
|
||||||
|
"description": "Container deployment, Docker Compose configuration, image creation, high-availability service deployment via Swarm and Kubernetes."
|
||||||
|
},
|
||||||
|
"Rust": {
|
||||||
|
"title": "Rust Projects",
|
||||||
|
"description": "Learning the language, creation of a REST API with Ntex, rewrite of the GitHub NTFY API in Rust."
|
||||||
|
},
|
||||||
|
"React": {
|
||||||
|
"title": "React Development",
|
||||||
|
"description": "This portfolio, Modelec website, Studysen mobile application, professional projects."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"contact": {
|
||||||
|
"title": "Contact",
|
||||||
|
"subtitle": "A question? A project? Feel free to contact me!",
|
||||||
|
"info": {
|
||||||
|
"title": "Contact information",
|
||||||
|
"email": "Email",
|
||||||
|
"status": "Status",
|
||||||
|
"statusValue": "ISEN student - Work-study at Horoquartz",
|
||||||
|
"response": "Response",
|
||||||
|
"responseValue": "Usually within 24h"
|
||||||
|
},
|
||||||
|
"subjects.title": "Topics of interest",
|
||||||
|
"subjects.list": [
|
||||||
"Web Development",
|
"Web Development",
|
||||||
"System Administration",
|
"System Administration",
|
||||||
"DevOps",
|
"DevOps",
|
||||||
@@ -63,19 +106,20 @@
|
|||||||
"Collaboration",
|
"Collaboration",
|
||||||
"Internship/Job"
|
"Internship/Job"
|
||||||
],
|
],
|
||||||
"contact.form.title": "Send me a message",
|
"form.title": "Send me a message",
|
||||||
"contact.form.name": "Full name",
|
"form.name": "Full name",
|
||||||
"contact.form.namePlaceholder": "Your name",
|
"form.namePlaceholder": "Your name",
|
||||||
"contact.form.email": "Email",
|
"form.email": "Email",
|
||||||
"contact.form.emailPlaceholder": "your.email@example.com",
|
"form.emailPlaceholder": "your.email@example.com",
|
||||||
"contact.form.subject": "Subject",
|
"form.subject": "Subject",
|
||||||
"contact.form.subjectPlaceholder": "Subject of your message",
|
"form.subjectPlaceholder": "Subject of your message",
|
||||||
"contact.form.message": "Message",
|
"form.message": "Message",
|
||||||
"contact.form.messagePlaceholder": "Your message...",
|
"form.messagePlaceholder": "Your message...",
|
||||||
"contact.form.send": "Send message",
|
"form.send": "Send message",
|
||||||
"contact.form.sending": "Sending...",
|
"form.sending": "Sending...",
|
||||||
"contact.form.success": "Message sent successfully! I will reply as soon as possible.",
|
"form.success": "Message sent successfully! I will reply as soon as possible.",
|
||||||
"contact.form.error": "An error occurred. Please try again later.",
|
"form.error": "An error occurred. Please try again later."
|
||||||
|
},
|
||||||
"experience": {
|
"experience": {
|
||||||
"title": "Career & Experience",
|
"title": "Career & Experience",
|
||||||
"description": "My professional and academic journey",
|
"description": "My professional and academic journey",
|
||||||
@@ -144,5 +188,16 @@
|
|||||||
"technologies": ["C++", "QT", "Raspberry Pi", "Linux"]
|
"technologies": ["C++", "QT", "Raspberry Pi", "Linux"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"github": {
|
||||||
|
"title": "GitHub Stats",
|
||||||
|
"description": "An overview of my activity and contributions on GitHub",
|
||||||
|
"stars": "Stars",
|
||||||
|
"forks": "Forks",
|
||||||
|
"commits": "Commits",
|
||||||
|
"languages": "Most used languages",
|
||||||
|
"legend": "Contribution calendar",
|
||||||
|
"totalCommits": "Total commits",
|
||||||
|
"activity": "Recent activity"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,80 +1,125 @@
|
|||||||
{
|
{
|
||||||
"about.title": "A propos de moi",
|
"about": {
|
||||||
"about.description": "Je suis étudiant en 4e année à l'ISEN Nantes en alternance chez Horoquartz. Je suis passionné par l'informatique. J'ai appris à coder en autodidacte et je suis actuellement en train d'apprendre le Rust et le Go. Je suis également passionné par l'électronique et le hardware. Je possède un homelab composé de 3 serveur, un DELL T320, un DELL T330 et un Dell Precision T3610 les 3 sous proxmox.",
|
"title": "A propos de moi",
|
||||||
"card.title": "Etudiant en 4e année a l'ISEN Nantes en alternance chez Horoquartz",
|
"description": "Je suis étudiant en 4e année à l'ISEN Nantes en alternance chez Horoquartz. Je suis passionné par l'informatique. J'ai appris à coder en autodidacte et je suis actuellement en train d'apprendre le Rust et le Go. Je suis également passionné par l'électronique et le hardware. Je possède un homelab composé de 3 serveur, un DELL T320, un DELL T330 et un Dell Precision T3610 les 3 sous proxmox."
|
||||||
"projects.title": "Mes projets",
|
},
|
||||||
"projects.Front end starter.description": "Mon starter personnel pour projet front end",
|
"card": {
|
||||||
"projects.Project C - ISEN CIR 1.description": "Projet de fin de 1ere année à l'ISEN Nantes",
|
"title": "Etudiant en 4e année a l'ISEN Nantes en alternance chez Horoquartz"
|
||||||
"projects.Projet robot.description": "Projet de robot avec le club Modelec ISEN pour la coupe de france de robotique (Developpement et déploiment sur Raspberry Pi)",
|
},
|
||||||
"projects.MercuryCloud.description": "Projet d'herbergeur de serveur de jeu et VPS. Poste de support technique, administrateur des service VPS, Game et web.",
|
"projects": {
|
||||||
"projects.Projet C++ - ISEN CIR 2.description": "Projet de fin de 4e semestre à l'ISEN Nantes. Création d'un jeu de type Tower Defense en C++ avec la librairie QT6.",
|
"title": "Mes projets",
|
||||||
"projects.Github NTFY.description": "Projet de notification pour les releases github et dockerhub qui envoie des notifications sur ntfy, gotify et discord.",
|
"Front end starter.description": "Mon starter personnel pour projet front end",
|
||||||
"projects.Projet C++ - ISEN CIPA 3.description": "Projet de jeu de simulation de comportement de banc de poisson en C++ avec la librairie SLD2, avec support du multijoueur.",
|
"Project C - ISEN CIR 1.description": "Projet de fin de 1ere année à l'ISEN Nantes",
|
||||||
"projects.Alternance Horoquartz.description": "Développement d'un système de mise à jour pour les produits Horoquartz.",
|
"Projet robot.description": "Projet de robot avec le club Modelec ISEN pour la coupe de france de robotique (Developpement et déploiment sur Raspberry Pi)",
|
||||||
"cv.title": "Mon CV",
|
"MercuryCloud.description": "Projet d'herbergeur de serveur de jeu et VPS. Poste de support technique, administrateur des service VPS, Game et web.",
|
||||||
"cv.path": "/CV-Felix-MARQUET.pdf",
|
"Projet C++ - ISEN CIR 2.description": "Projet de fin de 4e semestre à l'ISEN Nantes. Création d'un jeu de type Tower Defense en C++ avec la librairie QT6.",
|
||||||
"nav.about": "A propos de moi",
|
"Github NTFY.description": "Projet de notification pour les releases github et dockerhub qui envoie des notifications sur ntfy, gotify et discord.",
|
||||||
"nav.projects": "Mes projets",
|
"Projet C++ - ISEN CIPA 3.description": "Projet de jeu de simulation de comportement de banc de poisson en C++ avec la librairie SLD2, avec support du multijoueur.",
|
||||||
"nav.cv": "Mon CV",
|
"Alternance Horoquartz.description": "Développement d'un système de mise à jour pour les produits Horoquartz."
|
||||||
"skills.beginner": "Débutant",
|
},
|
||||||
"skills.intermediate": "Intermédiaire",
|
"cv": {
|
||||||
"skills.expert": "Expert",
|
"title": "Mon CV",
|
||||||
"skills.examples.C.title": "Projets C",
|
"path": "/CV-Felix-MARQUET.pdf"
|
||||||
"skills.examples.C.description": "Projet de fin de 1ère année à l'ISEN, algorithmes de base, création d'une api REST",
|
},
|
||||||
"skills.examples.C++.title": "Projets C++",
|
"nav": {
|
||||||
"skills.examples.C++.description": "Tower Defense en Qt6, simulation de banc de poissons avec SDL2 avec support du multijoueur, algorithmes de base",
|
"about": "A propos de moi",
|
||||||
"skills.examples.Admin Système.title": "Administration Système",
|
"projects": "Mes projets",
|
||||||
"skills.examples.Admin Système.description": "Configuration de 3 serveurs DELL sous Proxmox, virtualisation, maintenance des machines virtuelles, deploiement d'applications dans Azure",
|
"cv": "Mon CV",
|
||||||
"skills.examples.Python.title": "Projets Python",
|
"contact": "Contact",
|
||||||
"skills.examples.Python.description": "Github NTFY pour les notifications des releases github et dockerhub, projets de cours",
|
"experience": "Expérience",
|
||||||
"skills.examples.PHP.title": "Projets PHP",
|
"github": "GitHub"
|
||||||
"skills.examples.PHP.description": "Développements web avec PHP, AJAX, postgreSQL",
|
},
|
||||||
"skills.examples.HTML/CSS.title": "Développement Front-end",
|
"skills": {
|
||||||
"skills.examples.HTML/CSS.description": "Front-end starter personnalisé, projet de base, divers projets web",
|
"beginner": "Débutant",
|
||||||
"skills.examples.JS/TS.title": "JavaScript/TypeScript",
|
"intermediate": "Intermédiaire",
|
||||||
"skills.examples.JS/TS.description": "Développement React, ce portfolio, api NodeJS avec Express, Création d'une application mobile Studysen avec React Native, projets professionnels",
|
"expert": "Expert",
|
||||||
"skills.examples.Linux.title": "Administration Linux",
|
"examples": {
|
||||||
"skills.examples.Linux.description": "Configuration de serveurs, administration système, Ansible playbooks",
|
"C": {
|
||||||
"skills.examples.Go.title": "Projets Go",
|
"title": "Projets C",
|
||||||
"skills.examples.Go.description": "Initiation au langage, Création d'une api REST avec Fiber, Go Gin et la librairie standard, projets professionnels",
|
"description": "Projet de fin de 1ère année à l'ISEN, algorithmes de base, création d'une api REST"
|
||||||
"skills.examples.Docker.title": "Containerisation",
|
},
|
||||||
"skills.examples.Docker.description": "Déploiement de conteneurs, configuration Docker Compose, création d'images, déploiement de services en haute disponibilité via Swarm et Kubernetes",
|
"C++": {
|
||||||
"contact.title": "Contact",
|
"title": "Projets C++",
|
||||||
"contact.subtitle": "Une question ? Un projet ? N'hésitez pas à me contacter !",
|
"description": "Tower Defense en Qt6, simulation de banc de poissons avec SDL2 avec support du multijoueur, algorithmes de base"
|
||||||
"contact.info.title": "Informations de contact",
|
},
|
||||||
"contact.info.email": "Email",
|
"Admin Système": {
|
||||||
"contact.info.status": "Statut",
|
"title": "Administration Système",
|
||||||
"contact.info.statusValue": "Étudiant ISEN - Alternant chez Horoquartz",
|
"description": "Configuration de 3 serveurs DELL sous Proxmox, virtualisation, maintenance des machines virtuelles, deploiement d'applications dans Azure"
|
||||||
"contact.info.response": "Réponse",
|
},
|
||||||
"contact.info.responseValue": "Généralement sous 24h",
|
"Python": {
|
||||||
"contact.subjects.title": "Sujets d'intérêt",
|
"title": "Projets Python",
|
||||||
"contact.subjects.list": [
|
"description": "Github NTFY pour les notifications des releases github et dockerhub, projets de cours"
|
||||||
"Développement Web",
|
},
|
||||||
"Administration Système",
|
"PHP": {
|
||||||
"DevOps",
|
"title": "Projets PHP",
|
||||||
"Alternance",
|
"description": "Développements web avec PHP, AJAX, postgreSQL"
|
||||||
"Projets Open Source",
|
},
|
||||||
"Homelab",
|
"HTML/CSS": {
|
||||||
"Collaboration",
|
"title": "Développement Front-end",
|
||||||
"Stage/Emploi"
|
"description": "Front-end starter personnalisé, projet de base, divers projets web"
|
||||||
],
|
},
|
||||||
"contact.form.title": "Envoyez-moi un message",
|
"JS/TS": {
|
||||||
"contact.form.name": "Nom complet",
|
"title": "JavaScript/TypeScript",
|
||||||
"contact.form.namePlaceholder": "Votre nom",
|
"description": "Développement React, ce portfolio, api NodeJS avec Express, Création d'une application mobile Studysen avec React Native, projets professionnels"
|
||||||
"contact.form.email": "Email",
|
},
|
||||||
"contact.form.emailPlaceholder": "votre.email@exemple.com",
|
"Linux": {
|
||||||
"contact.form.subject": "Sujet",
|
"title": "Administration Linux",
|
||||||
"contact.form.subjectPlaceholder": "Objet de votre message",
|
"description": "Configuration de serveurs, administration système, Ansible playbooks"
|
||||||
"contact.form.message": "Message",
|
},
|
||||||
"contact.form.messagePlaceholder": "Votre message...",
|
"Go": {
|
||||||
"contact.form.send": "Envoyer le message",
|
"title": "Projets Go",
|
||||||
"contact.form.sending": "Envoi en cours...",
|
"description": "Initiation au langage, Création d'une api REST avec Fiber, Go Gin et la librairie standard, projets professionnels"
|
||||||
"contact.form.success": "Message envoyé avec succès ! Je vous répondrai dans les plus brefs délais.",
|
},
|
||||||
"contact.form.error": "Une erreur est survenue. Veuillez réessayer plus tard.",
|
"Docker": {
|
||||||
"skills.examples.Rust.title": "Projets Rust",
|
"title": "Containerisation",
|
||||||
"skills.examples.Rust.description": "Apprentissage du langage, création d'une api REST avec Ntex, récriture de l'api github NTFY en Rust",
|
"description": "Déploiement de conteneurs, configuration Docker Compose, création d'images, déploiement de services en haute disponibilité via Swarm et Kubernetes"
|
||||||
"skills.examples.React.title": "Développement React",
|
},
|
||||||
"skills.examples.React.description": "Ce portfolio, site web de Modelec, application mobile Studysen, projets professionnels",
|
"Rust": {
|
||||||
|
"title": "Projets Rust",
|
||||||
|
"description": "Apprentissage du langage, création d'une api REST avec Ntex, récriture de l'api github NTFY en Rust"
|
||||||
|
},
|
||||||
|
"React": {
|
||||||
|
"title": "Développement React",
|
||||||
|
"description": "Ce portfolio, site web de Modelec, application mobile Studysen, projets professionnels"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"contact": {
|
||||||
|
"title": "Contact",
|
||||||
|
"subtitle": "Une question ? Un projet ? N'hésitez pas à me contacter !",
|
||||||
|
"info": {
|
||||||
|
"title": "Informations de contact",
|
||||||
|
"email": "Email",
|
||||||
|
"status": "Statut",
|
||||||
|
"statusValue": "Étudiant ISEN - Alternant chez Horoquartz",
|
||||||
|
"response": "Réponse",
|
||||||
|
"responseValue": "Généralement sous 24h"
|
||||||
|
},
|
||||||
|
"subjects.title": "Sujets d'intérêt",
|
||||||
|
"subjects.list": [
|
||||||
|
"Développement Web",
|
||||||
|
"Administration Système",
|
||||||
|
"DevOps",
|
||||||
|
"Alternance",
|
||||||
|
"Projets Open Source",
|
||||||
|
"Homelab",
|
||||||
|
"Collaboration",
|
||||||
|
"Stage/Emploi"
|
||||||
|
],
|
||||||
|
"form.title": "Envoyez-moi un message",
|
||||||
|
"form.name": "Nom complet",
|
||||||
|
"form.namePlaceholder": "Votre nom",
|
||||||
|
"form.email": "Email",
|
||||||
|
"form.emailPlaceholder": "votre.email@exemple.com",
|
||||||
|
"form.subject": "Sujet",
|
||||||
|
"form.subjectPlaceholder": "Objet de votre message",
|
||||||
|
"form.message": "Message",
|
||||||
|
"form.messagePlaceholder": "Votre message...",
|
||||||
|
"form.send": "Envoyer le message",
|
||||||
|
"form.sending": "Envoi en cours...",
|
||||||
|
"form.success": "Message envoyé avec succès ! Je vous répondrai dans les plus brefs délais.",
|
||||||
|
"form.error": "Une erreur est survenue. Veuillez réessayer plus tard."
|
||||||
|
},
|
||||||
"experience": {
|
"experience": {
|
||||||
"title": "Parcours & Expérience",
|
"title": "Parcours & Expérience",
|
||||||
"description": "Mon évolution professionnelle et académique",
|
"description": "Mon évolution professionnelle et académique",
|
||||||
@@ -143,5 +188,16 @@
|
|||||||
"technologies": ["C++", "QT", "Raspberry Pi", "Linux"]
|
"technologies": ["C++", "QT", "Raspberry Pi", "Linux"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"github": {
|
||||||
|
"title": "Statistiques GitHub",
|
||||||
|
"description": "Un aperçu de mon activité et de mes contributions sur GitHub",
|
||||||
|
"stars": "Étoiles",
|
||||||
|
"forks": "Forks",
|
||||||
|
"commits": "Commits",
|
||||||
|
"languages": "Langages les plus utilisés",
|
||||||
|
"legend": "Calendrier des contributions",
|
||||||
|
"totalCommits": "Total des commits",
|
||||||
|
"activity": "Activité récente"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,21 +3,22 @@ import { FaSun, FaMoon} from "react-icons/fa";
|
|||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
import './output.css';
|
import './output.css';
|
||||||
import './other.css';
|
import './other.css';
|
||||||
import Card from "./components/Card.tsx";
|
import Card from "./components/Card";
|
||||||
import About from "./components/About.tsx";
|
import About from "./components/About";
|
||||||
import Skills from "./components/Skills.tsx";
|
import Skills from "./components/Skills";
|
||||||
import Project from "./components/Project.tsx";
|
import Project from "./components/Project";
|
||||||
import Footer from "./components/Footer.tsx";
|
import Footer from "./components/Footer";
|
||||||
import CV from "./components/CV.tsx";
|
import CV from "./components/CV";
|
||||||
import Menu from "./components/Menu.tsx";
|
import Menu from "./components/Menu";
|
||||||
import LoadingScreen from "./components/LoadingScreen.tsx";
|
import LoadingScreen from "./components/LoadingScreen";
|
||||||
import ParticlesBackground from "./components/ParticlesBackground.tsx";
|
import ParticlesBackground from "./components/ParticlesBackground";
|
||||||
import ContactSection from "./components/ContactSection.tsx";
|
import ContactSection from "./components/ContactSection";
|
||||||
import TimelineSection from "./components/TimelineSection.tsx";
|
import TimelineSection from "./components/TimelineSection";
|
||||||
import data from "./assets/DATA.ts";
|
import data from "./assets/DATA";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import i18n from './i18n.js';
|
import i18n from './i18n.js';
|
||||||
import {createRoot} from "react-dom/client";
|
import {createRoot} from "react-dom/client";
|
||||||
|
import GitHubStatsSection from 'components/GitHubStatsSection';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
// Initialise le thème depuis localStorage ou détecte le thème système
|
// Initialise le thème depuis localStorage ou détecte le thème système
|
||||||
@@ -78,7 +79,12 @@ function App() {
|
|||||||
<About />
|
<About />
|
||||||
<Skills skills={data.skills} />
|
<Skills skills={data.skills} />
|
||||||
<hr className="text-gray-800 dark:text-gray-200 mt-4" id="experience" />
|
<hr className="text-gray-800 dark:text-gray-200 mt-4" id="experience" />
|
||||||
<TimelineSection experience={data.experience} />
|
<TimelineSection experience={data.experience.map(item => ({
|
||||||
|
...item,
|
||||||
|
type: item.type as "work" | "education" | "achievement"
|
||||||
|
}))} />
|
||||||
|
<hr className="text-gray-800 dark:text-gray-200 mt-4" id="github" />
|
||||||
|
<GitHubStatsSection />
|
||||||
<hr className="text-gray-800 dark:text-gray-200 mt-4" id="projects" />
|
<hr className="text-gray-800 dark:text-gray-200 mt-4" id="projects" />
|
||||||
<Project projects={data.projects} />
|
<Project projects={data.projects} />
|
||||||
<hr className="text-gray-800 dark:text-gray-200 mt-4" id="contact" />
|
<hr className="text-gray-800 dark:text-gray-200 mt-4" id="contact" />
|
||||||
@@ -99,8 +105,10 @@ function App() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const container = document.getElementById('root');
|
const container = document.getElementById('root');
|
||||||
const root = createRoot(container);
|
if (container) {
|
||||||
root.render(<App />);
|
const root = createRoot(container);
|
||||||
|
root.render(<App />);
|
||||||
|
}
|
||||||
// ReactDOM.render(<App />, document.getElementById('root'));
|
// ReactDOM.render(<App />, document.getElementById('root'));
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
@@ -12,6 +12,7 @@ interface Social {
|
|||||||
|
|
||||||
interface CardProps {
|
interface CardProps {
|
||||||
name: string;
|
name: string;
|
||||||
|
title: string;
|
||||||
social: Social;
|
social: Social;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
234
src/components/GitHubStatsSection.tsx
Normal file
234
src/components/GitHubStatsSection.tsx
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { FaGithub, FaStar, FaCodeBranch, FaCode, FaCalendarAlt } from 'react-icons/fa';
|
||||||
|
|
||||||
|
interface GitHubStats {
|
||||||
|
totalRepos: number;
|
||||||
|
totalStars: number;
|
||||||
|
totalForks: number;
|
||||||
|
totalCommits: number;
|
||||||
|
languages: { [key: string]: number };
|
||||||
|
contributions: Array<{ date: string; count: number }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GitHubStatsSection: React.FC = () => {
|
||||||
|
const [stats, setStats] = useState<GitHubStats | null>(null);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchStats = async () => {
|
||||||
|
try {
|
||||||
|
const username = "BreizhHardware"; // à adapter si besoin
|
||||||
|
const token = import.meta.env.VITE_GITHUB_TOKEN;
|
||||||
|
// Infos utilisateur REST
|
||||||
|
const userRes = await fetch(`https://api.github.com/users/${username}`, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
Accept: "application/vnd.github.v3+json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const userData = await userRes.json();
|
||||||
|
|
||||||
|
// Repos pour stars/forks REST
|
||||||
|
const reposRes = await fetch(`https://api.github.com/users/${username}/repos?per_page=100`, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
Accept: "application/vnd.github.v3+json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const reposData = await reposRes.json();
|
||||||
|
|
||||||
|
// Calculs REST
|
||||||
|
const totalStars = reposData.reduce((acc: number, repo: any) => acc + repo.stargazers_count, 0);
|
||||||
|
const totalForks = reposData.reduce((acc: number, repo: any) => acc + repo.forks_count, 0);
|
||||||
|
// Langages
|
||||||
|
const langCount: { [key: string]: number } = {};
|
||||||
|
reposData.forEach((repo: any) => {
|
||||||
|
if (repo.language) {
|
||||||
|
langCount[repo.language] = (langCount[repo.language] || 0) + 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const totalRepos = userData.public_repos;
|
||||||
|
|
||||||
|
// GraphQL pour contributions
|
||||||
|
const today = new Date();
|
||||||
|
const lastYear = new Date(today);
|
||||||
|
lastYear.setFullYear(today.getFullYear() - 1);
|
||||||
|
const fromDate = lastYear.toISOString(); // format complet ISO 8601
|
||||||
|
const toDate = today.toISOString();
|
||||||
|
const graphQLQuery = {
|
||||||
|
query: `
|
||||||
|
query {
|
||||||
|
user(login: "${username}") {
|
||||||
|
contributionsCollection(from: \"${fromDate}\", to: \"${toDate}\") {
|
||||||
|
contributionCalendar {
|
||||||
|
totalContributions
|
||||||
|
weeks {
|
||||||
|
contributionDays {
|
||||||
|
date
|
||||||
|
contributionCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
};
|
||||||
|
const graphQLRes = await fetch("https://api.github.com/graphql", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(graphQLQuery),
|
||||||
|
});
|
||||||
|
const graphQLData = await graphQLRes.json();
|
||||||
|
const calendar = graphQLData.data.user.contributionsCollection.contributionCalendar;
|
||||||
|
const totalCommits = calendar.totalContributions;
|
||||||
|
// Flatten days
|
||||||
|
const contributions: Array<{ date: string; count: number }> = [];
|
||||||
|
calendar.weeks.forEach((week: any) => {
|
||||||
|
week.contributionDays.forEach((day: any) => {
|
||||||
|
contributions.push({ date: day.date, count: day.contributionCount });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
setStats({
|
||||||
|
totalRepos,
|
||||||
|
totalStars,
|
||||||
|
totalForks,
|
||||||
|
totalCommits,
|
||||||
|
languages: langCount,
|
||||||
|
contributions,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
setStats(null);
|
||||||
|
}
|
||||||
|
setLoading(false);
|
||||||
|
};
|
||||||
|
fetchStats();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const topLanguages = stats ? Object.entries(stats.languages).sort((a, b) => b[1] - a[1]).slice(0, 5) : [];
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<div className="max-w-4xl mx-auto mt-16">
|
||||||
|
<div className="bg-white dark:bg-gray-800 rounded-lg p-8 shadow-lg">
|
||||||
|
<div className="animate-pulse">
|
||||||
|
<div className="h-8 bg-gray-300 dark:bg-gray-600 rounded w-1/3 mx-auto mb-6"></div>
|
||||||
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||||
|
{[1, 2, 3, 4].map((i) => (
|
||||||
|
<div key={i} className="h-20 bg-gray-300 dark:bg-gray-600 rounded"></div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="max-w-4xl mx-auto mt-16">
|
||||||
|
<div className="text-center mb-8">
|
||||||
|
<h2 className="text-3xl md:text-4xl font-bold text-gray-800 dark:text-gray-200 mb-4 flex items-center justify-center gap-3">
|
||||||
|
{t('github.title')}
|
||||||
|
</h2>
|
||||||
|
<p className="text-lg text-gray-600 dark:text-gray-400">
|
||||||
|
{t('github.description')}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{stats && (
|
||||||
|
<div className="space-y-8">
|
||||||
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
|
||||||
|
<div className="bg-white dark:bg-gray-800 rounded-lg p-6 shadow-lg card-hover text-center">
|
||||||
|
<FaCode className="text-3xl text-blue-500 mx-auto mb-3" />
|
||||||
|
<p className="text-2xl font-bold text-gray-800 dark:text-gray-200">{stats.totalRepos}</p>
|
||||||
|
<p className="text-sm text-gray-600 dark:text-gray-400">{t('github.repositories')}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-white dark:bg-gray-800 rounded-lg p-6 shadow-lg card-hover text-center">
|
||||||
|
<FaStar className="text-3xl text-yellow-500 mx-auto mb-3" />
|
||||||
|
<p className="text-2xl font-bold text-gray-800 dark:text-gray-200">{stats.totalStars}</p>
|
||||||
|
<p className="text-sm text-gray-600 dark:text-gray-400">{t('github.stars')}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-white dark:bg-gray-800 rounded-lg p-6 shadow-lg card-hover text-center">
|
||||||
|
<FaCodeBranch className="text-3xl text-green-500 mx-auto mb-3" />
|
||||||
|
<p className="text-2xl font-bold text-gray-800 dark:text-gray-200">{stats.totalForks}</p>
|
||||||
|
<p className="text-sm text-gray-600 dark:text-gray-400">{t('github.forks')}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-white dark:bg-gray-800 rounded-lg p-6 shadow-lg card-hover text-center">
|
||||||
|
<FaCalendarAlt className="text-3xl text-purple-500 mx-auto mb-3" />
|
||||||
|
<p className="text-2xl font-bold text-gray-800 dark:text-gray-200">{stats.totalCommits !== -1 ? stats.totalCommits : "N/A"}</p>
|
||||||
|
<p className="text-sm text-gray-600 dark:text-gray-400">{t('github.commits')}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-white dark:bg-gray-800 rounded-lg p-6 shadow-lg card-hover">
|
||||||
|
<h3 className="text-xl font-bold text-gray-800 dark:text-gray-200 mb-6 text-center">
|
||||||
|
{t('github.languages')}
|
||||||
|
</h3>
|
||||||
|
<div className="space-y-4">
|
||||||
|
{topLanguages.map(([language, percentage], index) => (
|
||||||
|
<div key={language} className="animate-slideInLeft" style={{ animationDelay: `${index * 100}ms` }}>
|
||||||
|
<div className="flex justify-between items-center mb-2">
|
||||||
|
<span className="text-sm font-medium text-gray-700 dark:text-gray-300">{language}</span>
|
||||||
|
<span className="text-sm text-gray-500 dark:text-gray-400">{percentage}%</span>
|
||||||
|
</div>
|
||||||
|
<div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-3 overflow-hidden">
|
||||||
|
<div
|
||||||
|
className="h-full bg-gradient-to-r from-blue-500 to-purple-500 rounded-full progress-fill transition-all duration-1000 ease-out"
|
||||||
|
style={{
|
||||||
|
width: `${percentage}%`,
|
||||||
|
animationDelay: `${index * 200}ms`
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-white dark:bg-gray-800 rounded-lg p-6 shadow-lg card-hover">
|
||||||
|
<h3 className="text-xl font-bold text-gray-800 dark:text-gray-200 mb-6 text-center">
|
||||||
|
{t('github.activity')}
|
||||||
|
</h3>
|
||||||
|
{stats.contributions && (
|
||||||
|
<div className="grid grid-cols-12 gap-1 max-w-3xl mx-auto">
|
||||||
|
{stats.contributions.map((day, i) => {
|
||||||
|
let color = 'bg-gray-200 dark:bg-gray-700';
|
||||||
|
if (day.count > 10) color = 'bg-green-500';
|
||||||
|
else if (day.count > 5) color = 'bg-green-400';
|
||||||
|
else if (day.count > 2) color = 'bg-green-300';
|
||||||
|
else if (day.count > 0) color = 'bg-green-200';
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={day.date}
|
||||||
|
className={`w-3 h-3 rounded-sm ${color} animate-fadeInUp`}
|
||||||
|
style={{ animationDelay: `${i * 2}ms` }}
|
||||||
|
title={`${day.date}: ${day.count} commit${day.count > 1 ? 's' : ''}`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="text-center mt-4">
|
||||||
|
<span className="text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
{t('github.legend')}<br/>
|
||||||
|
{t('github.totalCommits')} : {stats.totalCommits}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GitHubStatsSection;
|
||||||
@@ -1,216 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
import { FaServer, FaDocker, FaLinux, FaNetworkWired, FaHdd, FaMicrochip } from 'react-icons/fa';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
interface ServerSpec {
|
|
||||||
name: string;
|
|
||||||
model: string;
|
|
||||||
cpu: string;
|
|
||||||
ram: string;
|
|
||||||
storage: string;
|
|
||||||
os: string;
|
|
||||||
services: string[];
|
|
||||||
status: 'online' | 'offline' | 'maintenance';
|
|
||||||
}
|
|
||||||
|
|
||||||
const HomelabSection: React.FC = () => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const [activeTab, setActiveTab] = useState<'overview' | 'servers' | 'services'>('overview');
|
|
||||||
|
|
||||||
const servers: ServerSpec[] = [
|
|
||||||
{
|
|
||||||
name: "DELL T320",
|
|
||||||
model: "PowerEdge T320",
|
|
||||||
cpu: "Intel Xeon E5-2400 series",
|
|
||||||
ram: "32GB DDR3",
|
|
||||||
storage: "4x 1TB HDD RAID 10",
|
|
||||||
os: "Proxmox VE",
|
|
||||||
services: ["Proxmox", "Docker", "VM Management", "Backup Server"],
|
|
||||||
status: 'online'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "DELL T330",
|
|
||||||
model: "PowerEdge T330",
|
|
||||||
cpu: "Intel Xeon E3-1200 series",
|
|
||||||
ram: "64GB DDR4",
|
|
||||||
storage: "6x 2TB HDD + 2x 500GB SSD",
|
|
||||||
os: "Proxmox VE",
|
|
||||||
services: ["Media Server", "Development Environment", "CI/CD", "Monitoring"],
|
|
||||||
status: 'online'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const services = [
|
|
||||||
{ name: "Proxmox Cluster", icon: <FaServer />, description: "Virtualisation et orchestration" },
|
|
||||||
{ name: "Docker Swarm", icon: <FaDocker />, description: "Conteneurisation des services" },
|
|
||||||
{ name: "Monitoring Stack", icon: <FaNetworkWired />, description: "Grafana + Prometheus + Alertmanager" },
|
|
||||||
{ name: "Backup Solution", icon: <FaHdd />, description: "Sauvegarde automatisée 3-2-1" },
|
|
||||||
{ name: "Development Lab", icon: <FaMicrochip />, description: "Environnements de développement isolés" },
|
|
||||||
{ name: "Media Center", icon: <FaLinux />, description: "Streaming et gestion multimédia" }
|
|
||||||
];
|
|
||||||
|
|
||||||
const getStatusColor = (status: string) => {
|
|
||||||
switch (status) {
|
|
||||||
case 'online': return 'bg-green-500';
|
|
||||||
case 'offline': return 'bg-red-500';
|
|
||||||
case 'maintenance': return 'bg-yellow-500';
|
|
||||||
default: return 'bg-gray-500';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getStatusText = (status: string) => {
|
|
||||||
switch (status) {
|
|
||||||
case 'online': return 'En ligne';
|
|
||||||
case 'offline': return 'Hors ligne';
|
|
||||||
case 'maintenance': return 'Maintenance';
|
|
||||||
default: return 'Inconnu';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="max-w-6xl mx-auto mt-16">
|
|
||||||
<div className="text-center mb-12">
|
|
||||||
<h2 className="text-3xl md:text-4xl font-bold text-gray-800 dark:text-gray-200 mb-4">
|
|
||||||
🏠 Mon Homelab
|
|
||||||
</h2>
|
|
||||||
<p className="text-lg text-gray-600 dark:text-gray-400">
|
|
||||||
Infrastructure personnelle et laboratoire de développement
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Navigation tabs */}
|
|
||||||
<div className="flex justify-center mb-8">
|
|
||||||
<div className="bg-gray-200 dark:bg-gray-700 rounded-lg p-1">
|
|
||||||
{['overview', 'servers', 'services'].map((tab) => (
|
|
||||||
<button
|
|
||||||
key={tab}
|
|
||||||
onClick={() => setActiveTab(tab as any)}
|
|
||||||
className={`px-6 py-2 rounded-md text-sm font-medium transition-all ${
|
|
||||||
activeTab === tab
|
|
||||||
? 'bg-white dark:bg-gray-600 text-gray-800 dark:text-gray-200 shadow-sm'
|
|
||||||
: 'text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{tab === 'overview' ? 'Vue d\'ensemble' : tab === 'servers' ? 'Serveurs' : 'Services'}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Content based on active tab */}
|
|
||||||
<div className="animate-fadeInUp">
|
|
||||||
{activeTab === 'overview' && (
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
|
||||||
<div className="bg-white dark:bg-gray-800 rounded-lg p-6 shadow-lg card-hover">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div>
|
|
||||||
<p className="text-2xl font-bold text-gray-800 dark:text-gray-200">2</p>
|
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-400">Serveurs</p>
|
|
||||||
</div>
|
|
||||||
<FaServer className="text-blue-500 text-2xl" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-white dark:bg-gray-800 rounded-lg p-6 shadow-lg card-hover">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div>
|
|
||||||
<p className="text-2xl font-bold text-gray-800 dark:text-gray-200">96GB</p>
|
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-400">RAM Total</p>
|
|
||||||
</div>
|
|
||||||
<FaMicrochip className="text-green-500 text-2xl" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-white dark:bg-gray-800 rounded-lg p-6 shadow-lg card-hover">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div>
|
|
||||||
<p className="text-2xl font-bold text-gray-800 dark:text-gray-200">16TB</p>
|
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-400">Stockage</p>
|
|
||||||
</div>
|
|
||||||
<FaHdd className="text-purple-500 text-2xl" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-white dark:bg-gray-800 rounded-lg p-6 shadow-lg card-hover">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div>
|
|
||||||
<p className="text-2xl font-bold text-gray-800 dark:text-gray-200">24/7</p>
|
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-400">Uptime</p>
|
|
||||||
</div>
|
|
||||||
<FaNetworkWired className="text-orange-500 text-2xl" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{activeTab === 'servers' && (
|
|
||||||
<div className="space-y-6">
|
|
||||||
{servers.map((server, index) => (
|
|
||||||
<div key={index} className="bg-white dark:bg-gray-800 rounded-lg shadow-lg card-hover overflow-hidden">
|
|
||||||
<div className="p-6">
|
|
||||||
<div className="flex items-center justify-between mb-4">
|
|
||||||
<div className="flex items-center space-x-3">
|
|
||||||
<FaServer className="text-2xl text-blue-500" />
|
|
||||||
<div>
|
|
||||||
<h3 className="text-xl font-bold text-gray-800 dark:text-gray-200">{server.name}</h3>
|
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-400">{server.model}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<div className={`w-3 h-3 rounded-full ${getStatusColor(server.status)}`}></div>
|
|
||||||
<span className="text-sm font-medium text-gray-600 dark:text-gray-400">
|
|
||||||
{getStatusText(server.status)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4">
|
|
||||||
<div>
|
|
||||||
<p className="text-sm font-medium text-gray-500 dark:text-gray-400">CPU</p>
|
|
||||||
<p className="text-sm text-gray-800 dark:text-gray-200">{server.cpu}</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p className="text-sm font-medium text-gray-500 dark:text-gray-400">RAM</p>
|
|
||||||
<p className="text-sm text-gray-800 dark:text-gray-200">{server.ram}</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p className="text-sm font-medium text-gray-500 dark:text-gray-400">Stockage</p>
|
|
||||||
<p className="text-sm text-gray-800 dark:text-gray-200">{server.storage}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<p className="text-sm font-medium text-gray-500 dark:text-gray-400 mb-2">Services</p>
|
|
||||||
<div className="flex flex-wrap gap-2">
|
|
||||||
{server.services.map((service, idx) => (
|
|
||||||
<span key={idx} className="px-3 py-1 text-xs bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200 rounded-full">
|
|
||||||
{service}
|
|
||||||
</span>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{activeTab === 'services' && (
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
||||||
{services.map((service, index) => (
|
|
||||||
<div key={index} className="bg-white dark:bg-gray-800 rounded-lg p-6 shadow-lg card-hover">
|
|
||||||
<div className="flex items-center space-x-3 mb-3">
|
|
||||||
<div className="text-2xl text-blue-500">{service.icon}</div>
|
|
||||||
<h3 className="text-lg font-semibold text-gray-800 dark:text-gray-200">{service.name}</h3>
|
|
||||||
</div>
|
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-400">{service.description}</p>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default HomelabSection;
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
|
|
||||||
interface LoadingScreenProps {
|
interface LoadingScreenProps {
|
||||||
onLoadingComplete: () => void;
|
onLoadingComplete: () => void;
|
||||||
@@ -9,24 +9,162 @@ const LoadingScreen: React.FC<LoadingScreenProps> = ({ onLoadingComplete }) => {
|
|||||||
const [showCursor, setShowCursor] = useState(true);
|
const [showCursor, setShowCursor] = useState(true);
|
||||||
const fullText = "Félix MARQUET";
|
const fullText = "Félix MARQUET";
|
||||||
const [isComplete, setIsComplete] = useState(false);
|
const [isComplete, setIsComplete] = useState(false);
|
||||||
|
const [assetsLoaded, setAssetsLoaded] = useState(false);
|
||||||
|
const [loadingProgress, setLoadingProgress] = useState(0);
|
||||||
|
const [currentAsset, setCurrentAsset] = useState('Initialisation...');
|
||||||
|
|
||||||
|
const typingRef = useRef<{
|
||||||
|
currentIndex: number;
|
||||||
|
timeout: NodeJS.Timeout | null;
|
||||||
|
isTyping: boolean;
|
||||||
|
}>({
|
||||||
|
currentIndex: 0,
|
||||||
|
timeout: null,
|
||||||
|
isTyping: false
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let currentIndex = 0;
|
let loadedCount = 0;
|
||||||
const typingInterval = setInterval(() => {
|
let totalCount = 0;
|
||||||
if (currentIndex <= fullText.length) {
|
|
||||||
setDisplayText(fullText.substring(0, currentIndex));
|
const updateProgress = () => {
|
||||||
currentIndex++;
|
const progress = totalCount > 0 ? (loadedCount / totalCount) * 100 : 0;
|
||||||
|
setLoadingProgress(progress);
|
||||||
|
|
||||||
|
if (loadedCount === totalCount && totalCount > 0) {
|
||||||
|
setAssetsLoaded(true);
|
||||||
|
setCurrentAsset('Prêt !');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const incrementLoaded = (assetName?: string) => {
|
||||||
|
loadedCount++;
|
||||||
|
if (assetName) {
|
||||||
|
setCurrentAsset(`Chargé: ${assetName}`);
|
||||||
|
}
|
||||||
|
updateProgress();
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkAssets = async () => {
|
||||||
|
const assetsToCheck = [
|
||||||
|
{ url: 'https://1.gravatar.com/avatar/4d43af207280d1d23e2a2905577c7b6167723fec2d33f946cc86f114c1a85b8d?size=256', name: 'Photo de profil' },
|
||||||
|
{ url: '/V5.png', name: 'Favicon' },
|
||||||
|
{ url: '/CV-Felix-MARQUET.pdf', name: 'CV Français' },
|
||||||
|
{ url: '/CV-Felix-MARQUET-English.pdf', name: 'CV Anglais' },
|
||||||
|
{ url: '/locales/fr/translation.json', name: 'Traductions FR' },
|
||||||
|
{ url: '/locales/en/translation.json', name: 'Traductions EN' }
|
||||||
|
];
|
||||||
|
|
||||||
|
totalCount = assetsToCheck.length + 1;
|
||||||
|
|
||||||
|
setCurrentAsset('Chargement des polices...');
|
||||||
|
try {
|
||||||
|
await document.fonts.ready;
|
||||||
|
incrementLoaded('Polices');
|
||||||
|
} catch (error) {
|
||||||
|
incrementLoaded('Polices');
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const asset of assetsToCheck) {
|
||||||
|
setCurrentAsset(`Chargement: ${asset.name}...`);
|
||||||
|
|
||||||
|
if (asset.url.endsWith('.json')) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(asset.url);
|
||||||
|
if (response.ok) {
|
||||||
|
incrementLoaded(asset.name);
|
||||||
|
} else {
|
||||||
|
incrementLoaded(asset.name);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
incrementLoaded(asset.name);
|
||||||
|
}
|
||||||
|
} else if (asset.url.endsWith('.pdf')) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(asset.url, { method: 'HEAD' });
|
||||||
|
incrementLoaded(asset.name);
|
||||||
|
} catch (error) {
|
||||||
|
incrementLoaded(asset.name);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await new Promise<void>((resolve) => {
|
||||||
|
const img = new Image();
|
||||||
|
img.onload = () => {
|
||||||
|
incrementLoaded(asset.name);
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
img.onerror = () => {
|
||||||
|
incrementLoaded(asset.name);
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
img.src = asset.url;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!assetsLoaded) {
|
||||||
|
loadedCount = totalCount;
|
||||||
|
setAssetsLoaded(true);
|
||||||
|
setLoadingProgress(100);
|
||||||
|
setCurrentAsset('Prêt !');
|
||||||
|
}
|
||||||
|
}, 5000);
|
||||||
|
};
|
||||||
|
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
checkAssets();
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
return () => clearTimeout(timeout);
|
||||||
|
}, [assetsLoaded]);
|
||||||
|
|
||||||
|
const startTyping = () => {
|
||||||
|
if (typingRef.current.isTyping) return;
|
||||||
|
|
||||||
|
typingRef.current.isTyping = true;
|
||||||
|
|
||||||
|
const typeNextChar = () => {
|
||||||
|
if (typingRef.current.currentIndex <= fullText.length) {
|
||||||
|
setDisplayText(fullText.substring(0, typingRef.current.currentIndex));
|
||||||
|
typingRef.current.currentIndex++;
|
||||||
|
|
||||||
|
const delay = assetsLoaded ? 30 : 150;
|
||||||
|
typingRef.current.timeout = setTimeout(typeNextChar, delay);
|
||||||
} else {
|
} else {
|
||||||
clearInterval(typingInterval);
|
|
||||||
setIsComplete(true);
|
setIsComplete(true);
|
||||||
|
typingRef.current.isTyping = false;
|
||||||
|
|
||||||
|
const finalDelay = assetsLoaded ? 200 : 800;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
onLoadingComplete();
|
onLoadingComplete();
|
||||||
}, 1000);
|
}, finalDelay);
|
||||||
}
|
}
|
||||||
}, 150);
|
};
|
||||||
|
|
||||||
|
typeNextChar();
|
||||||
|
};
|
||||||
|
|
||||||
return () => clearInterval(typingInterval);
|
useEffect(() => {
|
||||||
}, [fullText, onLoadingComplete]);
|
startTyping();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (typingRef.current.timeout) {
|
||||||
|
clearTimeout(typingRef.current.timeout);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (assetsLoaded && typingRef.current.isTyping && !isComplete) {
|
||||||
|
if (typingRef.current.timeout) {
|
||||||
|
clearTimeout(typingRef.current.timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
typingRef.current.isTyping = false;
|
||||||
|
setTimeout(() => startTyping(), 10);
|
||||||
|
}
|
||||||
|
}, [assetsLoaded, isComplete]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const cursorInterval = setInterval(() => {
|
const cursorInterval = setInterval(() => {
|
||||||
@@ -43,12 +181,15 @@ const LoadingScreen: React.FC<LoadingScreenProps> = ({ onLoadingComplete }) => {
|
|||||||
{displayText}
|
{displayText}
|
||||||
{showCursor && !isComplete && <span className="animate-pulse">|</span>}
|
{showCursor && !isComplete && <span className="animate-pulse">|</span>}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex space-x-2 justify-center">
|
<div className="flex space-x-2 justify-center mb-4">
|
||||||
<div className="w-3 h-3 bg-white rounded-full animate-bounce" style={{ animationDelay: '0ms' }}></div>
|
<div className="w-3 h-3 bg-white rounded-full animate-bounce" style={{ animationDelay: '0ms' }}></div>
|
||||||
<div className="w-3 h-3 bg-white rounded-full animate-bounce" style={{ animationDelay: '150ms' }}></div>
|
<div className="w-3 h-3 bg-white rounded-full animate-bounce" style={{ animationDelay: '150ms' }}></div>
|
||||||
<div className="w-3 h-3 bg-white rounded-full animate-bounce" style={{ animationDelay: '300ms' }}></div>
|
<div className="w-3 h-3 bg-white rounded-full animate-bounce" style={{ animationDelay: '300ms' }}></div>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-white/80 mt-4 text-lg">Chargement du portfolio...</p>
|
|
||||||
|
<p className="text-white/80 text-lg">
|
||||||
|
{assetsLoaded ? 'Prêt !' : currentAsset}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -9,9 +9,10 @@ function Menu() {
|
|||||||
<ul className="flex justify-around">
|
<ul className="flex justify-around">
|
||||||
<li><a href="#top" className="text-gray-800 dark:text-gray-200 hover:text-blue-500 dark:hover:text-blue-400 transition-colors">Félix MARQUET</a></li>
|
<li><a href="#top" className="text-gray-800 dark:text-gray-200 hover:text-blue-500 dark:hover:text-blue-400 transition-colors">Félix MARQUET</a></li>
|
||||||
<li><a href="#about" className="text-gray-800 dark:text-gray-200 hover:text-blue-500 dark:hover:text-blue-400 transition-colors">{t('nav.about')}</a></li>
|
<li><a href="#about" className="text-gray-800 dark:text-gray-200 hover:text-blue-500 dark:hover:text-blue-400 transition-colors">{t('nav.about')}</a></li>
|
||||||
<li><a href="#experience" className="text-gray-800 dark:text-gray-200 hover:text-blue-500 dark:hover:text-blue-400 transition-colors">Expérience</a></li>
|
<li><a href="#experience" className="text-gray-800 dark:text-gray-200 hover:text-blue-500 dark:hover:text-blue-400 transition-colors">{t('nav.experience')}</a></li>
|
||||||
|
<li><a href="#github" className="text-gray-800 dark:text-gray-200 hover:text-blue-500 dark:hover:text-blue-400 transition-colors">{t('nav.github')}</a></li>
|
||||||
<li><a href="#projects" className="text-gray-800 dark:text-gray-200 hover:text-blue-500 dark:hover:text-blue-400 transition-colors">{t('nav.projects')}</a></li>
|
<li><a href="#projects" className="text-gray-800 dark:text-gray-200 hover:text-blue-500 dark:hover:text-blue-400 transition-colors">{t('nav.projects')}</a></li>
|
||||||
<li><a href="#contact" className="text-gray-800 dark:text-gray-200 hover:text-blue-500 dark:hover:text-blue-400 transition-colors">Contact</a></li>
|
<li><a href="#contact" className="text-gray-800 dark:text-gray-200 hover:text-blue-500 dark:hover:text-blue-400 transition-colors">{t('nav.contact')}</a></li>
|
||||||
<li><a href="#cv" className="text-gray-800 dark:text-gray-200 hover:text-blue-500 dark:hover:text-blue-400 transition-colors">{t('nav.cv')}</a></li>
|
<li><a href="#cv" className="text-gray-800 dark:text-gray-200 hover:text-blue-500 dark:hover:text-blue-400 transition-colors">{t('nav.cv')}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
131
src/output.css
131
src/output.css
@@ -17,6 +17,7 @@
|
|||||||
--color-green-50: oklch(98.2% 0.018 155.826);
|
--color-green-50: oklch(98.2% 0.018 155.826);
|
||||||
--color-green-100: oklch(96.2% 0.044 156.743);
|
--color-green-100: oklch(96.2% 0.044 156.743);
|
||||||
--color-green-200: oklch(92.5% 0.084 155.995);
|
--color-green-200: oklch(92.5% 0.084 155.995);
|
||||||
|
--color-green-300: oklch(87.1% 0.15 154.449);
|
||||||
--color-green-500: oklch(72.3% 0.219 149.579);
|
--color-green-500: oklch(72.3% 0.219 149.579);
|
||||||
--color-green-800: oklch(44.8% 0.119 151.328);
|
--color-green-800: oklch(44.8% 0.119 151.328);
|
||||||
--color-green-900: oklch(39.3% 0.095 152.535);
|
--color-green-900: oklch(39.3% 0.095 152.535);
|
||||||
@@ -46,6 +47,7 @@
|
|||||||
--color-white: #fff;
|
--color-white: #fff;
|
||||||
--spacing: 0.25rem;
|
--spacing: 0.25rem;
|
||||||
--container-xs: 20rem;
|
--container-xs: 20rem;
|
||||||
|
--container-3xl: 48rem;
|
||||||
--container-4xl: 56rem;
|
--container-4xl: 56rem;
|
||||||
--container-6xl: 72rem;
|
--container-6xl: 72rem;
|
||||||
--text-xs: 0.75rem;
|
--text-xs: 0.75rem;
|
||||||
@@ -71,9 +73,11 @@
|
|||||||
--font-weight-bold: 700;
|
--font-weight-bold: 700;
|
||||||
--font-weight-extrabold: 800;
|
--font-weight-extrabold: 800;
|
||||||
--leading-relaxed: 1.625;
|
--leading-relaxed: 1.625;
|
||||||
|
--radius-sm: 0.25rem;
|
||||||
--radius-md: 0.375rem;
|
--radius-md: 0.375rem;
|
||||||
--radius-lg: 0.5rem;
|
--radius-lg: 0.5rem;
|
||||||
--radius-xl: 0.75rem;
|
--radius-xl: 0.75rem;
|
||||||
|
--ease-out: cubic-bezier(0, 0, 0.2, 1);
|
||||||
--animate-spin: spin 1s linear infinite;
|
--animate-spin: spin 1s linear infinite;
|
||||||
--animate-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
--animate-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||||
--animate-bounce: bounce 1s infinite;
|
--animate-bounce: bounce 1s infinite;
|
||||||
@@ -397,9 +401,18 @@
|
|||||||
.h-6 {
|
.h-6 {
|
||||||
height: calc(var(--spacing) * 6);
|
height: calc(var(--spacing) * 6);
|
||||||
}
|
}
|
||||||
|
.h-8 {
|
||||||
|
height: calc(var(--spacing) * 8);
|
||||||
|
}
|
||||||
|
.h-20 {
|
||||||
|
height: calc(var(--spacing) * 20);
|
||||||
|
}
|
||||||
.h-64 {
|
.h-64 {
|
||||||
height: calc(var(--spacing) * 64);
|
height: calc(var(--spacing) * 64);
|
||||||
}
|
}
|
||||||
|
.h-full {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
.min-h-screen {
|
.min-h-screen {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
@@ -409,6 +422,9 @@
|
|||||||
.w-1 {
|
.w-1 {
|
||||||
width: calc(var(--spacing) * 1);
|
width: calc(var(--spacing) * 1);
|
||||||
}
|
}
|
||||||
|
.w-1\/3 {
|
||||||
|
width: calc(1/3 * 100%);
|
||||||
|
}
|
||||||
.w-3 {
|
.w-3 {
|
||||||
width: calc(var(--spacing) * 3);
|
width: calc(var(--spacing) * 3);
|
||||||
}
|
}
|
||||||
@@ -430,12 +446,12 @@
|
|||||||
.w-full {
|
.w-full {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
.max-w-3xl {
|
||||||
|
max-width: var(--container-3xl);
|
||||||
|
}
|
||||||
.max-w-4xl {
|
.max-w-4xl {
|
||||||
max-width: var(--container-4xl);
|
max-width: var(--container-4xl);
|
||||||
}
|
}
|
||||||
.max-w-6xl {
|
|
||||||
max-width: var(--container-6xl);
|
|
||||||
}
|
|
||||||
.max-w-xs {
|
.max-w-xs {
|
||||||
max-width: var(--container-xs);
|
max-width: var(--container-xs);
|
||||||
}
|
}
|
||||||
@@ -470,6 +486,12 @@
|
|||||||
.grid-cols-1 {
|
.grid-cols-1 {
|
||||||
grid-template-columns: repeat(1, minmax(0, 1fr));
|
grid-template-columns: repeat(1, minmax(0, 1fr));
|
||||||
}
|
}
|
||||||
|
.grid-cols-2 {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
.grid-cols-12 {
|
||||||
|
grid-template-columns: repeat(12, minmax(0, 1fr));
|
||||||
|
}
|
||||||
.flex-col {
|
.flex-col {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
@@ -500,6 +522,9 @@
|
|||||||
.gap-2 {
|
.gap-2 {
|
||||||
gap: calc(var(--spacing) * 2);
|
gap: calc(var(--spacing) * 2);
|
||||||
}
|
}
|
||||||
|
.gap-3 {
|
||||||
|
gap: calc(var(--spacing) * 3);
|
||||||
|
}
|
||||||
.gap-4 {
|
.gap-4 {
|
||||||
gap: calc(var(--spacing) * 4);
|
gap: calc(var(--spacing) * 4);
|
||||||
}
|
}
|
||||||
@@ -526,13 +551,6 @@
|
|||||||
margin-block-end: calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)));
|
margin-block-end: calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.space-y-6 {
|
|
||||||
:where(& > :not(:last-child)) {
|
|
||||||
--tw-space-y-reverse: 0;
|
|
||||||
margin-block-start: calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse));
|
|
||||||
margin-block-end: calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.space-y-8 {
|
.space-y-8 {
|
||||||
:where(& > :not(:last-child)) {
|
:where(& > :not(:last-child)) {
|
||||||
--tw-space-y-reverse: 0;
|
--tw-space-y-reverse: 0;
|
||||||
@@ -571,6 +589,9 @@
|
|||||||
.overflow-hidden {
|
.overflow-hidden {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
.rounded {
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
}
|
||||||
.rounded-full {
|
.rounded-full {
|
||||||
border-radius: calc(infinity * 1px);
|
border-radius: calc(infinity * 1px);
|
||||||
}
|
}
|
||||||
@@ -580,6 +601,9 @@
|
|||||||
.rounded-md {
|
.rounded-md {
|
||||||
border-radius: var(--radius-md);
|
border-radius: var(--radius-md);
|
||||||
}
|
}
|
||||||
|
.rounded-sm {
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
}
|
||||||
.rounded-xl {
|
.rounded-xl {
|
||||||
border-radius: var(--radius-xl);
|
border-radius: var(--radius-xl);
|
||||||
}
|
}
|
||||||
@@ -642,9 +666,6 @@
|
|||||||
.bg-gray-400 {
|
.bg-gray-400 {
|
||||||
background-color: var(--color-gray-400);
|
background-color: var(--color-gray-400);
|
||||||
}
|
}
|
||||||
.bg-gray-500 {
|
|
||||||
background-color: var(--color-gray-500);
|
|
||||||
}
|
|
||||||
.bg-gray-600 {
|
.bg-gray-600 {
|
||||||
background-color: var(--color-gray-600);
|
background-color: var(--color-gray-600);
|
||||||
}
|
}
|
||||||
@@ -654,6 +675,12 @@
|
|||||||
.bg-green-100 {
|
.bg-green-100 {
|
||||||
background-color: var(--color-green-100);
|
background-color: var(--color-green-100);
|
||||||
}
|
}
|
||||||
|
.bg-green-200 {
|
||||||
|
background-color: var(--color-green-200);
|
||||||
|
}
|
||||||
|
.bg-green-300 {
|
||||||
|
background-color: var(--color-green-300);
|
||||||
|
}
|
||||||
.bg-green-500 {
|
.bg-green-500 {
|
||||||
background-color: var(--color-green-500);
|
background-color: var(--color-green-500);
|
||||||
}
|
}
|
||||||
@@ -663,19 +690,21 @@
|
|||||||
.bg-red-100 {
|
.bg-red-100 {
|
||||||
background-color: var(--color-red-100);
|
background-color: var(--color-red-100);
|
||||||
}
|
}
|
||||||
.bg-red-500 {
|
|
||||||
background-color: var(--color-red-500);
|
|
||||||
}
|
|
||||||
.bg-white {
|
.bg-white {
|
||||||
background-color: var(--color-white);
|
background-color: var(--color-white);
|
||||||
}
|
}
|
||||||
.bg-yellow-500 {
|
|
||||||
background-color: var(--color-yellow-500);
|
|
||||||
}
|
|
||||||
.bg-gradient-to-br {
|
.bg-gradient-to-br {
|
||||||
--tw-gradient-position: to bottom right in oklab;
|
--tw-gradient-position: to bottom right in oklab;
|
||||||
background-image: linear-gradient(var(--tw-gradient-stops));
|
background-image: linear-gradient(var(--tw-gradient-stops));
|
||||||
}
|
}
|
||||||
|
.bg-gradient-to-r {
|
||||||
|
--tw-gradient-position: to right in oklab;
|
||||||
|
background-image: linear-gradient(var(--tw-gradient-stops));
|
||||||
|
}
|
||||||
|
.from-blue-500 {
|
||||||
|
--tw-gradient-from: var(--color-blue-500);
|
||||||
|
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
|
||||||
|
}
|
||||||
.from-blue-900 {
|
.from-blue-900 {
|
||||||
--tw-gradient-from: var(--color-blue-900);
|
--tw-gradient-from: var(--color-blue-900);
|
||||||
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
|
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
|
||||||
@@ -689,6 +718,10 @@
|
|||||||
--tw-gradient-to: var(--color-indigo-900);
|
--tw-gradient-to: var(--color-indigo-900);
|
||||||
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
|
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
|
||||||
}
|
}
|
||||||
|
.to-purple-500 {
|
||||||
|
--tw-gradient-to: var(--color-purple-500);
|
||||||
|
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
|
||||||
|
}
|
||||||
.p-1 {
|
.p-1 {
|
||||||
padding: calc(var(--spacing) * 1);
|
padding: calc(var(--spacing) * 1);
|
||||||
}
|
}
|
||||||
@@ -707,6 +740,9 @@
|
|||||||
.p-6 {
|
.p-6 {
|
||||||
padding: calc(var(--spacing) * 6);
|
padding: calc(var(--spacing) * 6);
|
||||||
}
|
}
|
||||||
|
.p-8 {
|
||||||
|
padding: calc(var(--spacing) * 8);
|
||||||
|
}
|
||||||
.px-2 {
|
.px-2 {
|
||||||
padding-inline: calc(var(--spacing) * 2);
|
padding-inline: calc(var(--spacing) * 2);
|
||||||
}
|
}
|
||||||
@@ -719,9 +755,6 @@
|
|||||||
.px-5 {
|
.px-5 {
|
||||||
padding-inline: calc(var(--spacing) * 5);
|
padding-inline: calc(var(--spacing) * 5);
|
||||||
}
|
}
|
||||||
.px-6 {
|
|
||||||
padding-inline: calc(var(--spacing) * 6);
|
|
||||||
}
|
|
||||||
.py-1 {
|
.py-1 {
|
||||||
padding-block: calc(var(--spacing) * 1);
|
padding-block: calc(var(--spacing) * 1);
|
||||||
}
|
}
|
||||||
@@ -834,9 +867,6 @@
|
|||||||
.text-green-800 {
|
.text-green-800 {
|
||||||
color: var(--color-green-800);
|
color: var(--color-green-800);
|
||||||
}
|
}
|
||||||
.text-orange-500 {
|
|
||||||
color: var(--color-orange-500);
|
|
||||||
}
|
|
||||||
.text-purple-500 {
|
.text-purple-500 {
|
||||||
color: var(--color-purple-500);
|
color: var(--color-purple-500);
|
||||||
}
|
}
|
||||||
@@ -855,6 +885,9 @@
|
|||||||
color: color-mix(in oklab, var(--color-white) 80%, transparent);
|
color: color-mix(in oklab, var(--color-white) 80%, transparent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.text-yellow-500 {
|
||||||
|
color: var(--color-yellow-500);
|
||||||
|
}
|
||||||
.opacity-0 {
|
.opacity-0 {
|
||||||
opacity: 0%;
|
opacity: 0%;
|
||||||
}
|
}
|
||||||
@@ -866,10 +899,6 @@
|
|||||||
--tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 2px 4px -2px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
|
--tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 2px 4px -2px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
|
||||||
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
||||||
}
|
}
|
||||||
.shadow-sm {
|
|
||||||
--tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
|
|
||||||
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
|
||||||
}
|
|
||||||
.shadow-xl {
|
.shadow-xl {
|
||||||
--tw-shadow: 0 20px 25px -5px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 8px 10px -6px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
|
--tw-shadow: 0 20px 25px -5px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 8px 10px -6px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
|
||||||
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
||||||
@@ -908,6 +937,10 @@
|
|||||||
--tw-duration: 1000ms;
|
--tw-duration: 1000ms;
|
||||||
transition-duration: 1000ms;
|
transition-duration: 1000ms;
|
||||||
}
|
}
|
||||||
|
.ease-out {
|
||||||
|
--tw-ease: var(--ease-out);
|
||||||
|
transition-timing-function: var(--ease-out);
|
||||||
|
}
|
||||||
.hover\:bg-blue-700 {
|
.hover\:bg-blue-700 {
|
||||||
&:hover {
|
&:hover {
|
||||||
@media (hover: hover) {
|
@media (hover: hover) {
|
||||||
@@ -929,13 +962,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.hover\:text-gray-800 {
|
|
||||||
&:hover {
|
|
||||||
@media (hover: hover) {
|
|
||||||
color: var(--color-gray-800);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.hover\:text-white {
|
.hover\:text-white {
|
||||||
&:hover {
|
&:hover {
|
||||||
@media (hover: hover) {
|
@media (hover: hover) {
|
||||||
@@ -1033,14 +1059,9 @@
|
|||||||
width: calc(3/4 * 100%);
|
width: calc(3/4 * 100%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.md\:grid-cols-2 {
|
.md\:grid-cols-4 {
|
||||||
@media (width >= 48rem) {
|
@media (width >= 48rem) {
|
||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
}
|
|
||||||
}
|
|
||||||
.md\:grid-cols-3 {
|
|
||||||
@media (width >= 48rem) {
|
|
||||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.md\:flex-row {
|
.md\:flex-row {
|
||||||
@@ -1090,16 +1111,6 @@
|
|||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.lg\:grid-cols-3 {
|
|
||||||
@media (width >= 64rem) {
|
|
||||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.lg\:grid-cols-4 {
|
|
||||||
@media (width >= 64rem) {
|
|
||||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.dark\:border-gray-600 {
|
.dark\:border-gray-600 {
|
||||||
&:where(.dark, .dark *) {
|
&:where(.dark, .dark *) {
|
||||||
border-color: var(--color-gray-600);
|
border-color: var(--color-gray-600);
|
||||||
@@ -1238,15 +1249,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.dark\:hover\:text-gray-200 {
|
|
||||||
&:where(.dark, .dark *) {
|
|
||||||
&:hover {
|
|
||||||
@media (hover: hover) {
|
|
||||||
color: var(--color-gray-200);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.dark\:hover\:text-gray-800 {
|
.dark\:hover\:text-gray-800 {
|
||||||
&:where(.dark, .dark *) {
|
&:where(.dark, .dark *) {
|
||||||
&:hover {
|
&:hover {
|
||||||
@@ -1442,6 +1444,10 @@
|
|||||||
syntax: "*";
|
syntax: "*";
|
||||||
inherits: false;
|
inherits: false;
|
||||||
}
|
}
|
||||||
|
@property --tw-ease {
|
||||||
|
syntax: "*";
|
||||||
|
inherits: false;
|
||||||
|
}
|
||||||
@property --tw-scale-x {
|
@property --tw-scale-x {
|
||||||
syntax: "*";
|
syntax: "*";
|
||||||
inherits: false;
|
inherits: false;
|
||||||
@@ -1541,6 +1547,7 @@
|
|||||||
--tw-backdrop-saturate: initial;
|
--tw-backdrop-saturate: initial;
|
||||||
--tw-backdrop-sepia: initial;
|
--tw-backdrop-sepia: initial;
|
||||||
--tw-duration: initial;
|
--tw-duration: initial;
|
||||||
|
--tw-ease: initial;
|
||||||
--tw-scale-x: 1;
|
--tw-scale-x: 1;
|
||||||
--tw-scale-y: 1;
|
--tw-scale-y: 1;
|
||||||
--tw-scale-z: 1;
|
--tw-scale-z: 1;
|
||||||
|
|||||||
Reference in New Issue
Block a user